4

C++ reference types as instance variables are forbidden in Objective-C++. How can I work around this?

4 Answers 4

9

You can't sensibly use references as instance variable because there is no way to initialize instance variables and references can't be reseated.

An alternative might be to simply use (possibly smart) pointers instead.

Another possibility that gets you closer to C++-like behaviour is to use a PIMPL-style member for your C++ members:

struct CppImpl {
    SomeClass& ref;
    CppImpl(SomeClass& ref) : ref(ref) {}
};

@interface A : NSObject {
    CppImpl* pimpl;    
}
- (id)initWithRef:(SomeClass&)ref;
@end

@implementation    
- (id)initWithRef:(SomeClass&)ref {
    if(self = [super init]) {
        pimpl = new CppImpl(ref);
    }
    return self;
}
// clean up CppImpl in dealloc etc. ...
@end
Sign up to request clarification or add additional context in comments.

8 Comments

I don't see why you don't just store a pointer to SomeClass itself. The difference between a reference and a pointer is (mostly) one of semantics which your interface transports perfectly. See my answer.
@Sebastian This is moot if you init with just one member (how often do you only have one member?). It gets interesting if you init with more than one member and want proper deterministic behavior back. FWIW, i mentioned pointers and this approach as possible options.
What's the problem with storing several pointers instead of the references? Where's the problem with determinism?
@Sebastian If you have N C++ objects your ObjC wrapper owns, do you intend to delete/de-init/... each of them manually?
@GeorgFritzsche Probably not. But that's not the case here anyway. A doesn't own ref. You've created the need to implement dealloc yourself and have at least solved a problem more complicated than what the question asked for.
|
2

Georg's first sentence is entirely correct:

You can't sensibly use references as instance variable because there is no way to initialize instance variables and references can't be reseated.

But I don't think his solution is the best one.

The semantic difference between a pointer and a reference is small. A reference is essentially a pointer that can't be null. So it is certainly a good idea to use references in your interface to make it very obvious that a nullptr is not a valid initialiser argument. But internally, you can simply store a pointer:

@interface A : NSObject {
    SomeClass* p;    
}
- (id)initWithRef:(SomeClass&)ref;
@end

@implementation A
- (id)initWithRef:(SomeClass&)ref {
    if(self = [super init]) {
        p = &ref;
    }
    return self;
}
@end

There's no more (in the worst case: manual) memory allocation, no resource handling at all, no additional indirection etc. Every member of A can simply assert that p != nullptr.

2 Comments

Asserting in every member of A seems cumbersome and error prone, if you can patters that require you to assert/check only once are preferrable.
That's no different than asserting that your pimpl pointer is still valid in each member. Maybe it's being too paranoid but it applies to both solutions.
0

boost::ref() may help?

1 Comment

Not really - reference_wrapper<T> has no default constructor and you can't initialize instance variables.
0

A more generic solution is to use reference_wrapper<T> instead of a custom struct. The end result is similar.

Then again, if you only need to store one member, you're not getting much advantage over pointers by going with either the struct or this wrapper. (Thanks Georg!)

I've used Georg's answer as a starting point for the example:

// This bare interface can be used from regular Objective-C code,
// useful to pass around as an opaque handle
@interface A : NSObject
@end

// This interface can be shown to appropriate Objective-C++ code
@interface A (Private) // @interface A () if it's just for this class's .mm file
- (id)initWithRef:(SomeClass &)ref;
@property (readonly, nonatomic) SomeClass &ref;
@end


@implementation A {
    reference_wrapper<SomeClass> *_refWrapper;    
}

- (id)init {
    // and/or throw an exception
    return nil;
}

- (id)initWithRef:(SomeClass &)ref {
    self = [super init];
    if(self) {
        _refWrapper = new reference_wrapper<SomeClass>(ref);
    }
    return self;
}

- (SomeClass &)ref {
    // reference_wrapper<T> is implicitly convertible to T&
    return *_refWrapper;
    // This would also work:
    // return _refWrapper->get();
}

- (void)dealloc {
    delete _refWrapper;
}

@end

This multiple header pattern is helpful to pass around an opaque handle in Objective-C code, while providing Objective-C++ features to a select few (even if it's just that objc class's implementation).

2 Comments

Why not store a (possibly smart) pointer to the instance directly here? I don't think the wrapper gains you anything in this scenario.
Why not indeed. Come to think of it, in situations actually as simple as the example, I've generally just stored a pointer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.