objective-ccocos2d-iphoneobjective-c++box2d-iphone

declare obj-c class interface that contain c++ class type ivar


Currently I am working on a cocos2d+Box2D project so I have deal with some Objective-C++ code.

And I am facing to such situation:

#import "cocos2d.h"
#import "Box2D.h"

@interface BasicNode : CCNode {
@private
    ccColor3B _color;
    b2Body *_body;
    b2Fixture *_shape;
}

b2Body and b2Fixture are C++ class that defined in Box2D.h

It works if the implementation of BasicNode is named BasicNode.mm.

But if I have another file named Game.m that is using BasicNode and import BasicNode.h, it won't compile because .m file is Obj-C file and does not know about C++ code.

So I decided to move #import "Box2D.h" into implementation file and only keep type declaration in head file (this is exactly what header file should contain).

But how do I do it? They are C++ class type but they are actually just a pointer so I wrote some helper macro

#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) struct clsname; typedef struct clsname clsname
#endif

CLS_DEF(b2Body);
CLS_DEF(b2Fixture);

It works, only if CLS_DEF(b2Body) is appear once only. Otherwise compiler will find multiple type declaration for a same name even they are the same. Than I have to change to

#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) @class clsname
#endif

And it is working now.

But I don't think it is a great idea that I declare a C++ class type as an Obj-C class especially I am using ARC.

Is any better way do deal with it? And I don't really want to make something like this

@interface BasicNode : CCNode {
@private
    ccColor3B _color;
#ifdef __cplusplus
    b2Body *_body;
    b2Fixture *_shape;
#else
    void *_body;
    void *_shape;
#endif
}

Edit: Also please tell me will my tweak way introduce any problem?? by making C++ class ivar looks like Obj-C class for other pure Obj-C code.


Solution

  • There are a couple of ways. If you can rely on using the Objective-C 2.2 runtime's features, you can add ivars in class (category) extensions. This means you can add ivars in your class's .mm file, and keep the .h file clean of any C++ stuff.

    If you need to support older versions of the runtime, there are a few ways to do it which are better than #ifdefing. In my opinion, the best way is to use the 'pimpl' idiom which is common in C++ - you forward declare an implementation struct in your header, and add an ivar which is a pointer to such a struct. In your class's implementation (.mm), you actually define that struct with all its C++ members. You then just need to allocate that implementation object in your init... method(s) with new and delete it in dealloc.

    I've written up the pimpl idiom as it applies to cleanly mixing Objective-C and C++ in this article - it also shows some other potential solutions which you could consider.