5

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?

harms
  • 8,728
  • 4
  • 30
  • 27
zoul
  • 102,279
  • 44
  • 260
  • 354
  • 1
    It's basically what I have come up with when I had the same problem. But pay attention to your ifdef: you have to insert padding for the non-cpp branch. Otherwise the compiler wouldn't know the size of your Foo objects. While this might not break on non-fragile instance var builds, it's definitely a problem for old style targets. – Nikolai Ruhe Jun 29 '09 at 08:15
  • I copied the approach you outlined above. Seemed pretty nice and easy, but then it caused some crazy memory corruption problems: http://stackoverflow.com/questions/2458652/objective-c-insanity-simple-assignement-to-a-single-float-variable-results – morgancodes Mar 18 '10 at 20:57

5 Answers5

8

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.

zoul
  • 102,279
  • 44
  • 260
  • 354
Barry Wark
  • 107,306
  • 24
  • 181
  • 206
  • 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 Feb 08 '10 at 09:05
  • 1
    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 Feb 08 '10 at 18:47
  • 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 Mar 18 '10 at 21:49
  • @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 Mar 18 '10 at 22:33
3

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
  • 68,394
  • 30
  • 171
  • 212
1

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
  • 17,664
  • 2
  • 43
  • 56
  • 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 Jul 02 '09 at 05:58
0

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
  • 856
  • 5
  • 10
0

DO NOT DO THIS

If you ifdef out an instance variable, that will give two separate instance variable layouts for this class. You will get random memory smashers all over the place because memory allocated for the object in half the cases will be too short. Instead of ifdefing out the instance variable, forward-declare its type like

struct CPPClass;

and have a pointer to it in the ivar, then in your init/dealloc methods call new/delete to create the object. If you have several objects, you can create a struct to hold all C++ ivars directly and then just new/delete that struct.

See this thread for more detail and further links to information, including a podcast that talks at length about ObjC++: Can I separate C++ main function and classes from Objective-C and/or C routines at compile and link?

Community
  • 1
  • 1
uliwitness
  • 8,532
  • 36
  • 58
  • In C, a struct type would have to be referred to as `struct CPPClass`, not `CPPClass`, so they would have to change that too. – newacct Feb 12 '15 at 22:29
  • In the ivar declaration, yeah, good catch. To C++ struct Foo and Foo are the same though, so it's only needed in the header. Usually I just write struct in front of the class name everywhere I use it, you don't need a separate struct declaration like I mentioned here on its own. – uliwitness Feb 13 '15 at 17:16