views:

613

answers:

5

Hello!

I need to mix Objective-C and C++. I would like to hide all the C++ stuff inside one class and keep all the others plain Objective-C. The problem is that I want to have some C++ classes as instance variables. This means they have to be mentioned in the header file, which gets included by other classes and C++ starts spreading to the whole application. The best solution I was able to come with so far looks like this:

#ifdef __cplusplus
#import "cppheader.h"
#endif

@interface Foo : NSObject
{
    id regularObjectiveCProperty;
    #ifdef __cplusplus
    CPPClass cppStuff;
    #endif
}

@end

This works. The implementation file has an mm extension, so that it gets compiled as Objective-C mixed with C++, the #ifdef unlocks the C++ stuff and there we go. When some other, purely Objective-C class imports the header, the C++ stuff is hidden and the class does not see anything special. This looks like a hack, is there a better solution?

+4  A: 

This sounds like a classic use for an interface/@protocol. Define an objective-c protocol for the API and then provide an implementation of that protocol using your Objective-C++ class. This way clients need only know about the protocol and not the header of the implementation. So given the original implementation

@interface Foo : NSObject
{
    id regularObjectiveCProperty;
    CPPClass cppStuff;

}

@end

I would define a protocol

//Extending the NSObject protocol gives the NSObject
// protocol methods. If not all implementations are
// descended from NSObject, skip this.
@protocol IFoo <NSObject>

// Foo methods here
@end

and modify the original Foo declaration to

@interface Foo : NSObject <IFoo>
{
    id regularObjectiveCProperty;
    CPPClass cppStuff;
}

@end

Client code can then work with type id<IFoo> and does not need to be compiled as Objective-C++. Obviously you can pass an instance of Foo to these clients.

Barry Wark
If the @interface Foo is in .h file, it that possible to preventing influencing of obj-c++?If the @interface Foo is in .mm file, how can I make an instance of it?
Eonil
Any module that imports Foo.h will have to be compiled as Objective-C++, but client code need import *only* IFoo's header to use IFoo and can thus be Objective-C only. This is a classic dependency management pattern.
Barry Wark
@Barry Wark -- in your example, how would one instantiate a new Foo without importing the header? The first thing that comes to mind is an Objective C++ factory class that doesn't import and C++ stuff into its header. It all seems very complicated. There's also this approach: http://stackoverflow.com/questions/2262011/adding-c-object-to-objective-c-class/2262395 but I haven't gotten it to work (see http://stackoverflow.com/questions/2463970/trouble-using-opaque-pointers-in-objective-c)
morgancodes
@mrogancodes A factory class that imports only the `IFoo` header would be the obvious choice as you state. There's nothing stopping you from using the PIMPL pattern in your Objective-C++ class, making the header C++-free...
Barry Wark
+1  A: 

Is there some particular reason you cannot just use Objective C++ for everything? Simply switch the compiler to Compile Sources As: Objective C++ (or rename all your source files from .cpp or .m to .mm). Then you can freely intermix your C++ and Objective C.

C++ starts spreading to the whole application

What problem is there with that? If your Objective C code is doing only C/Objective C code in general, then it will almost certainly not be affected at all by being compiled as C++. There is no appreciable size or speed performance issues.

The only two downsides I've found are: you cannot (yet) use clang static analyser to analyseC++; some (relatively weird) C code wont work in C++, which is occasionally an issue when using third party C code.

Peter N Lewis
The only problem is that I am not comfortable with C++ and I was afraid there are going to be some subtle differences that could lead to bugs. I like the idea of C++ being encapsulated only in the class that really needs it. But thank you, this certainly is a solution.
zoul
A: 

You might find that you have problems doing this -- from what I remember of ObjectiveC++ you may find that the constructor and the destructor for your enclosed C++ object won't get called.

Darren Ford
+1  A: 

I also ran into this issue recently. In my case a protocol was overkill. I just needed to keep a pointer to a data access object that happened to be a C++ object.

What I did was declare the class with a void * instance variable and cast it when I use it in the instance methods.

This is a little bit hack-y, but conceptually, it's very similar to what the Objective-C id type is.

Ben S
A: 

In this case, I think your best solution is to forward-declare the object, declare the member as a pointer to the cpp object, and manage it's life manually. The compiler does not need to know much more than a name and size, until you begin using the object. It is nearly identical in implementation to the void* comment, but you actually use the class name and avoid strict aliasing issues (if you use that). It is not a hack, but you do need to manage your memory manually.

Finally, cpp constructors and destructors are called properly when you have cpp members in objc classes, provided you have enabled the compiler option. The compiler generates hidden objc methods which get called. This was added years ago - but it is an 'option'.

#ifdef __cplusplus
#import "cppheader.h"
#else
struct CPPClass;
#endif

@interface Foo : NSObject
{
    id regularObjectiveCProperty;
    CPPClass* cppStuff;
}

@end
Justin