Synthesized ivar for std::tr1::shared_ptr<MyClass>?

  • I'm no C++ guru, so I'm hoping someone can help me by explaining why
    the following gives a compiler error:

    //MyObjC.h
    @interface MyObjC : NSObject {}
    @end

    //MyObjC.mm
    #import "MyObjC.h"
    #include "MyClass.h" //provides MyClass, a C++ class
    #include <tr1/memory>

    using std::tr1::shared_ptr;

    @interface MyObjC ()

    @property (assign, readwrite, nonatomic) shared_ptr<MyClass> foo;

    @end

    @implementation MyObjC
    @synthesize foo; // gives compiler error: instance variable 'foo' has
    unknown size

    @end

    The definitions for both shared_ptr and MyClass are available, as far
    as I can tell. Furthermore, the following compiles without error:
    //MyObjC.h
    #include <tr1/memory>
    #include "MyClass.h"

    @interface MyObjC : NSObject
    {
    std::tr1::shared_ptr<MyClass> foo;
    }
    @end

    //MyObjC.mm
    #import "MyObjC.h"
    #include "MyClass.h" //provides MyClass, a C++ class
    #include <tr1/memory>

    using std::tr1::shared_ptr;

    @interface MyObjC ()

    @property (assign, readwrite, nonatomic) shared_ptr<MyClass> foo;

    @end

    @implementation MyObjC
    @synthesize foo; // gives compiler error: instance variable 'foo' has
    unknown size

    @end

    Thus, I'm lead to believe this is an issue with runtime synthesizing
    of the ivar, but I'm at a loss to explain why it should be so. I would
    like to keep the C++ out of the header file if possible so that I can
    use standard Objective-C when importing the header file in client
    modules.

    Any guidance from C++ gurus?

    Thanks,
    Barry
  • On May 6, 2010, at 8:19 AM, Barry Wark wrote:
    > Thus, I'm lead to believe this is an issue with runtime synthesizing
    > of the ivar, but I'm at a loss to explain why it should be so. I would
    > like to keep the C++ out of the header file if possible so that I can
    > use standard Objective-C when importing the header file in client
    > modules.

    I don't have a solution. My guess is this is simply an ObjC++ compiler bug.

    However, I can offer a workaround: Put your ivars in a struct, put a pointer to the forward-declared struct in your header as the sole ivar. In your implementation file, define the struct and use the 'new' operator in the init method to allocate the contents of the struct (which will also call all constructors). In the dealloc method, delete the struct (and set the ivar to zero for safety).

    It's a little bit of added manual memory management, but from then on you can just add C++ ivars to the struct, as you'd do with the class. Just beware that you have to initialize all ivars, as you are not guaranteed to get zeroed-out memory back from C++ new (unlike ObjC's alloc).

    -- Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
  • On Thu, May 6, 2010 at 2:19 AM, Barry Wark <barrywark...> wrote:
    > I'm no C++ guru, so I'm hoping someone can help me by explaining why
    > the following gives a compiler error:

    I'm no guru either, but my guess would be that the interaction between
    synthesized ivars and C++ templates is rocky territory. I avoid going
    down that path, declare the ivar as a pointer (with an #ifdef to
    declare it as a void* for ObjC client code) and create the object with
    new in +init. Here's a snippet from a random number generator class I
    wrote, that uses Boost's random module "under the hood":

    In SPRandom.h:

    #ifdef __cplusplus
    #import <boost/random.hpp>
    #endif

    typedef struct {
        int min;
        int max;
    } SPRandomRange;

    @interface SPRandom : NSObject {
    #ifdef __cplusplus
        boost::uniform_int<> *distribution;
        boost::variate_generator<boost::lagged_fibonacci607&,
    boost::uniform_int<> > *generator;
    #else
        void *distribution;
        void *generator;
    #endif
    }
    ...
    @end

    In SPRandom.m:

    static boost::lagged_fibonacci607 rng;

    @implementation SPRandom

    - (id) initWithRange:(SPRandomRange)range {
        self = [super init];
        if (self) {
            distribution = new boost::uniform_int<>(range.min,range.max);
            generator = new
    boost::variate_generator<boost::lagged_fibonacci607&,
    boost::uniform_int<> >(rng, *distribution);
        }
        return self;
    }

    - (void) dealloc {
        delete generator;
        delete distribution;

        [super dealloc];
    }

    @end

    sherm--

    --
    Cocoa programming in Perl:
    http://www.camelbones.org
  • Thank you all for the suggestions. Using manual dynamic memory
    allocation somewhat defeats the purpose of using smart pointers, but
    it looks like that's the only option for now. For reference, I've
    filed this as rdar://7953539, but would be happy to be shown the error
    of my ways.

    Cheers,
    Barry

    On Thu, May 6, 2010 at 4:28 AM, Sherm Pendley <sherm.pendley...> wrote:
    > On Thu, May 6, 2010 at 2:19 AM, Barry Wark <barrywark...> wrote:
    >> I'm no C++ guru, so I'm hoping someone can help me by explaining why
    >> the following gives a compiler error:
    >
    > I'm no guru either, but my guess would be that the interaction between
    > synthesized ivars and C++ templates is rocky territory. I avoid going
    > down that path, declare the ivar as a pointer (with an #ifdef to
    > declare it as a void* for ObjC client code) and create the object with
    > new in +init. Here's a snippet from a random number generator class I
    > wrote, that uses Boost's random module "under the hood":
    >
    > In SPRandom.h:
    >
    > #ifdef __cplusplus
    > #import <boost/random.hpp>
    > #endif
    >
    > typedef struct {
    >    int min;
    >    int max;
    > } SPRandomRange;
    >
    > @interface SPRandom : NSObject {
    > #ifdef __cplusplus
    >    boost::uniform_int<> *distribution;
    >    boost::variate_generator<boost::lagged_fibonacci607&,
    > boost::uniform_int<> > *generator;
    > #else
    >    void *distribution;
    >    void *generator;
    > #endif
    > }
    > ...
    > @end
    >
    > In SPRandom.m:
    >
    > static boost::lagged_fibonacci607 rng;
    >
    > @implementation SPRandom
    >
    > - (id) initWithRange:(SPRandomRange)range {
    >    self = [super init];
    >    if (self) {
    >        distribution = new boost::uniform_int<>(range.min,range.max);
    >        generator = new
    > boost::variate_generator<boost::lagged_fibonacci607&,
    > boost::uniform_int<> >(rng, *distribution);
    >    }
    >    return self;
    > }
    >
    > - (void) dealloc {
    >    delete generator;
    >    delete distribution;
    >
    >    [super dealloc];
    > }
    >
    > @end
    >
    > sherm--
    >
    > --
    > Cocoa programming in Perl:
    > http://www.camelbones.org
    >
  • On Thu, May 6, 2010 at 4:18 PM, Barry Wark <barrywark...> wrote:
    > Thank you all for the suggestions. Using manual dynamic memory
    > allocation somewhat defeats the purpose of using smart pointers, but
    > it looks like that's the only option for now. For reference, I've
    > filed this as rdar://7953539, but would be happy to be shown the error
    > of my ways.

    It looks like there's just bad news all around with incomplete types
    and synthesized instance variables. I found clang crashes when told to
    synthesize an instance variable of incomplete type. I wouldn't be
    surprised if the interactions between ivar synthesis and the type
    system were not completely nailed down.

    For reference, the clang bug is http://llvm.org/bugs/show_bug.cgi?idp64

    --Kyle Sluder
  • On Thu, May 6, 2010 at 4:38 PM, Kyle Sluder <kyle.sluder...> wrote:
    > On Thu, May 6, 2010 at 4:18 PM, Barry Wark <barrywark...> wrote:
    >> Thank you all for the suggestions. Using manual dynamic memory
    >> allocation somewhat defeats the purpose of using smart pointers, but
    >> it looks like that's the only option for now. For reference, I've
    >> filed this as rdar://7953539, but would be happy to be shown the error
    >> of my ways.
    >
    > It looks like there's just bad news all around with incomplete types
    > and synthesized instance variables. I found clang crashes when told to
    > synthesize an instance variable of incomplete type. I wouldn't be
    > surprised if the interactions between ivar synthesis and the type
    > system were not completely nailed down.
    >
    > For reference, the clang bug is http://llvm.org/bugs/show_bug.cgi?idp64

    Kyle,

    Your comment brings things into better focus; it appears that template
    instantiation is happening after the compiler attempts to synthesize
    the ivar and accessor methods. Again, my C++-foo is weak but I recall
    that template instantiation happens as a separate compilation step,
    no? Too bad; it's a shame to have to resort to allocating a shared_ptr
    on the heap just to play nicely with modern C++ code that's using RAII
    (including smart pointers) to manage memory.

    -Barry

    >
    > --Kyle Sluder
    >
  • On Fri, May 7, 2010 at 11:47 AM, Barry Wark <barrywark...> wrote:
    > Your comment brings things into better focus; it appears that template
    > instantiation is happening after the compiler attempts to synthesize
    > the ivar and accessor methods. Again, my C++-foo is weak but I recall
    > that template instantiation happens as a separate compilation step,
    > no? Too bad; it's a shame to have to resort to allocating a shared_ptr
    > on the heap just to play nicely with modern C++ code that's using RAII
    > (including smart pointers) to manage memory.

    My C++-foo is similarly weak, but this explanation seems logical if a
    bit unfortunate. After all, nothing stops you from creating auto or
    static variables of template type, right? So the compiler needs to
    specialize templates as it compiles other code; @synthesize
    declarations should be no different. Since Cocoa doesn't follow RAII
    anyway, I'm not all that distressed.

    Perhaps you could provide a public abstract class and hide the
    C++-using implementation behind it? I can't think of another solution
    that doesn't break the 32-bit runtime.

    --Kyle Sluder
  • On May 6, 2010, at 4:18 PM, Barry Wark wrote:
    > Thank you all for the suggestions. Using manual dynamic memory
    > allocation somewhat defeats the purpose of using smart pointers, but
    > it looks like that's the only option for now. For reference, I've
    > filed this as rdar://7953539, but would be happy to be shown the error
    > of my ways.

    Is there some reason why you can't declare your instance variables explicitly, and tell @synthesize to use that ivar in the synthesized accessor methods?

    --
    Greg Parker    <gparker...>    Runtime Wrangler
  • On Fri, May 7, 2010 at 12:29 PM, Greg Parker <gparker...> wrote:
    > Is there some reason why you can't declare your instance variables explicitly, and tell @synthesize to use that ivar in the synthesized accessor methods?

    He's trying to keep C++ out of his ObjC headers.

    --Kyle Sluder
  • On May 7, 2010, at 12:33 PM, Kyle Sluder wrote:
    > On Fri, May 7, 2010 at 12:29 PM, Greg Parker <gparker...> wrote:
    >> Is there some reason why you can't declare your instance variables explicitly, and tell @synthesize to use that ivar in the synthesized accessor methods?
    >
    > He's trying to keep C++ out of his ObjC headers.

    In that case, just stick an #ifdef __cplusplus around the instance variable declarations. Your non-C++ files won't see the ivars, but the non-fragile ivar support in the modern runtime means that they don't have to. It won't work on 32-bit Mac, but automatically synthesized ivars don't work there either.

    --
    Greg Parker    <gparker...>    Runtime Wrangler
  • On Fri, May 7, 2010 at 12:37 PM, Greg Parker <gparker...> wrote:
    > On May 7, 2010, at 12:33 PM, Kyle Sluder wrote:
    >> On Fri, May 7, 2010 at 12:29 PM, Greg Parker <gparker...> wrote:
    >>> Is there some reason why you can't declare your instance variables explicitly, and tell @synthesize to use that ivar in the synthesized accessor methods?
    >>
    >> He's trying to keep C++ out of his ObjC headers.
    >
    > In that case, just stick an #ifdef __cplusplus around the instance variable declarations. Your non-C++ files won't see the ivars, but the non-fragile ivar support in the modern runtime means that they don't have to. It won't work on 32-bit Mac, but automatically synthesized ivars don't work there either.

    Yes! That's the ticket. I hadn't realized that the runtime still
    provided non-fragile ivar support even when you explicitly declared
    the ivar. Leave it to the runtime guru. Thanks, Greg.

    -Barry
    >
    >
    > --
    > Greg Parker     <gparker...>     Runtime Wrangler
    >
    >
    >
  • On May 7, 2010, at 4:25 PM, Barry Wark wrote:
    > Yes! That's the ticket. I hadn't realized that the runtime still
    > provided non-fragile ivar support even when you explicitly declared
    > the ivar. Leave it to the runtime guru. Thanks, Greg.

    That's right. On iPhone OS devices and 64-bit Mac, all ivars are non-fragile. No exceptions[1]. Auto-synthesized property ivars are intended to shorten your code, and provide one way to move ivars out of header files for better encapsulation. (Other ways to keep ivars out of header files are in the works, for cases that are too simple or too complex to use synthesized properties.)

    [1] Exception: isa. It still needs to be at offset zero.

    --
    Greg Parker    <gparker...>    Runtime Wrangler
previous month may 2010 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
Go to today