views:

61

answers:

2

We all know how sparsely documented Objective-C++ is. I can't find anything on this, but I'm also finding it hard to find suitable unambiguous search terms. So, Stackoverflow to the rescue (I hope)!

I have a C++ class that I have an instance of within an Objective-C(++) class (and I have the project setting enabled for constructors/ destructors to be called).

This all works fine until I try to expose the instance via a @synthesized property. I make it an assign property (as Obj-C retain counting is not applicable). The property appears to work except when I set the instance I would expect the copy constructor to be involved. What I actually see is that a temporary instance is created (the copy constructor on that is invoked) - which is all expected. But the copy constructor on the ivar instance is not called. The values are "magically" set. I'm presuming that the @synthesized code is doing something like a memcpy as the final step. This is fine for C structs, but not so helpful for C++ classes where the correctness of the code depends on the copy constructors and assignment operators being called appropriately.

Has anyone looked into this in any more depth, got it working, or confirmed that it is not possible to hold C++ objects as ivars in an Obj-C(++) class and have copy constructors called by @synthesized property setters?

(I can post sample code for all this if necessary - but even the minimal version is a screenful or so).

A: 

The general rule with properties is, if you need specific behaviour that the default boilerplate that gets generated cannot provide you, write your own -setFoo: method for your foo property which does what you need it to. Automatic synthesis of properties are meant for the common case, they are not going to be applicable everywhere. In those cases, provide your own setter or getter for the property.

jer
What I'm really after is confirmation that this is how the generated property code actually works. It's not much of a stretch to expect it to behave that way. If it doesn't it's quite a problem for what I'm trying to do
Phil Nash
@Phil Nash: why is it a problem? You just need to write your own setter.
JeremyP
Because my C++ class is "library" code that should be used all over the place. If synthesized properties don't work correctly wrt assignment then it's game over for my library.
Phil Nash
+2  A: 

Uhm, if I'm not confused, it's perfectly reasonable that the copy constructor is not called. Do you expect that ivar to be magically destructed and copy-constructed? I guess that's against C++'s rules. If the synthesized setter is conceptually

-(void)setFoo:(Foo)foo_{
    foo=foo_;
}

then operator= should be called, rather than the copy constructor.

That said, the operator= is not called either, bummer (10.6.4, gcc 4.2.1.)! Here's the sample code:

#import <Foundation/Foundation.h>

class Foo{
    int a,b;
public:
    Foo():a(0),b(0){NSLog(@"default constructor for %p",this);}
    Foo(const Foo&foo):a(foo.a),b(foo.b){NSLog(@"copy constructor for %p",this);}
    Foo& operator=(const Foo& foo){
    NSLog(@"assignment operator for %p",this);
        a=foo.a;
        b=foo.b;
        return *this;
    }
};

@interface Bar:NSObject {
    Foo foo;
}
@property (assign) Foo foo;
@end

@implementation Bar
@synthesize foo;
@end

int main(){
    NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
    Bar* bar=[[Bar alloc] init];
    Foo foo;
    bar.foo=foo;
    [pool drain];
}

Save it into boo.mm, I had:

$ g++ -fobjc-call-cxx-cdtors boo.mm -framework Foundation
$ ./a.out
2010-09-04 12:32:06.570 a.out[24352:903] default constructor for 0x10010cdc8
2010-09-04 12:32:06.572 a.out[24352:903] default constructor for 0x7fff5fbff7e0
2010-09-04 12:32:06.573 a.out[24352:903] copy constructor for 0x7fff5fbff7d0
$

Now, I had the explicit setter I wrote above instead of @synthesize Foo, I correctly had

$ ./a.out
2010-09-04 12:42:22.206 a.out[24417:903] default constructor for 0x10010cdc8
2010-09-04 12:42:22.209 a.out[24417:903] default constructor for 0x7fff5fbff7e0
2010-09-04 12:42:22.210 a.out[24417:903] copy constructor for 0x7fff5fbff7d0
2010-09-04 12:42:22.210 a.out[24417:903] assignment operator for 0x10010cdc8

The clang source code for the generation of the synthesized setter is this, see GenerateObjCSetter. There, it eventually tests whether C++ assignment operator is needed. I would say it's a gray area, not well-documented but currently being implemented...

Yuji
Yes, sorry, I should have said copy constructor *or* assignment operator. I was watching both just in case :-) The copy constructor was called when creating the temp.
Phil Nash
So are you just confirming by experiment what I am seeing, or have you read somewhere that this is expected?
Phil Nash
I just observed what you said. I don't find any explicit documentation. You can check the source code of gcc or clang to see what they do.
Yuji
Found the corresponding code in clang repository, apparently the latest clang does generate the assignment operator, see the edited response. Anyway I don't think it's wise to rely on a not-well-documented cutting-edge technology...
Yuji
Thanks Yuji. Sounds like it's not safe to rely on this right now - which is a real shame for my library. The worst thing is I thought I had this working - but I had an error in my test that was hiding it. So I got quite far into my implementation before hitting this.
Phil Nash