目前我正在开发一个 cocos2d+Box2D 项目,所以我处理了一些 Objective-C++ 代码。
我面临着这样的情况:
#import "cocos2d.h"
#import "Box2D.h"
@interface BasicNode : CCNode {
@private
ccColor3B _color;
b2Body *_body;
b2Fixture *_shape;
}
b2Body和b2Fixture是在Box2D.h中定义的C++类,
如果BasicNode的实现被命名为BasicNode.mm,它就可以工作。
但是,如果我有另一个名为 Game.m 的文件,该文件正在使用 BasicNode 并导入 BasicNode.h,则它将无法编译,因为 .m 文件是 Obj-C 文件并且不了解 C++ 代码。
因此,我决定将 #import "Box2D.h" 移至实现文件中,并仅在头文件中保留类型声明(这正是头文件应包含的内容)。
但我该怎么做呢?它们是 C++ 类类型,但它们实际上只是一个指针,所以我编写了一些辅助宏。
#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);
只有当 CLS_DEF(b2Body) 只出现一次时,它才有效。否则编译器会发现同名的多个类型声明,即使它们是相同的。比我必须更改为
#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) @class clsname
#endif
现在它正在工作。
但我认为将 C++ 类类型声明为 Obj-C 类并不是一个好主意,尤其是我正在使用 ARC。
有什么更好的办法处理吗?我真的不想做这样的事情
@interface BasicNode : CCNode {
@private
ccColor3B _color;
#ifdef __cplusplus
b2Body *_body;
b2Fixture *_shape;
#else
void *_body;
void *_shape;
#endif
}
编辑:另外请告诉我我的调整方式会引入任何问题吗?通过使 C++ 类 ivar 看起来像其他纯 Obj-C 代码的 Obj-C 类。
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.
发布评论
评论(3)
一种简单的解决方案是将
Game.m
重命名为Game.mm
。One simple solution is to rename
Game.m
toGame.mm
.有几种方法。如果您可以依赖使用 Objective-C 2.2 运行时的功能,则可以在类(类别)扩展中添加 ivars。这意味着您可以在类的 .mm 文件中添加 ivars,并保持 .h 文件中不含任何 C++ 内容。
如果您需要支持旧版本的运行时,有几种方法可以比#ifdef更好。在我看来,最好的方法是使用 C++ 中常见的“pimpl”习惯用法 - 在标头中转发声明一个实现结构,并添加一个 ivar,它是指向此类结构的指针。在类的实现 (.mm) 中,您实际上定义了该结构及其所有 C++ 成员。然后,您只需在
init...
方法中使用new
分配该实现对象,并在dealloc 中
。delete
它我已经写了 pimpl 习惯用法,因为它适用于在 这篇文章 - 它还展示了您可以考虑的一些其他潜在解决方案。
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
#ifdef
ing. 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 yourinit...
method(s) withnew
anddelete
it indealloc
.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.
使用 Xcode 5,您不必在头文件中声明实例变量,只需在实现文件中声明它们即可。所以你的BasicNode头文件没有被C++“污染”。
在 C++ 中可以使用“struct”代替“class”。唯一的区别是,在类中,所有成员默认都是私有的,而在结构中,它们默认是公共的。但是您可以使用结构来完成您可以使用类完成的所有操作。这样,您就可以
在界面外部和
界面内进行编写。
With Xcode 5, you don't have to declare instance variables in the header file, you can just declare them in the implementation file. So your BasicNode header file is not "contaminated" with C++.
You can use "struct" instead of "class" in C++. The only difference is that in a class all members are private by default, while in a struct they are public by default. But you can do everything with a struct that you can do with a class. That way you can write for example
outside your interface, and
in your interface.