【问题标题】:How to detect C++ compiler with macro in Xcode?如何在 Xcode 中使用宏检测 C++ 编译器?
【发布时间】:2011-08-06 09:31:30
【问题描述】:

我在一个 iOS 项目中混合了 Objective-C (*.m) 和 Objective-C++ (*.mm) 源文件。 当我在 *.m 文件中导入 C++ 头文件时,如何排除头文件中的 C++ 特定代码?我想使用编译器宏,例如:

// SomeClass.h - a file I want to import in C++ and Objectice-C classes

#if CPLUSPLUS
#import "CPlusPlusLibrary.h"
#endif 

@interface SomeClass : BaseClass
{

#if CPLUSPLUS
  CPlusPlusClass* variable;
#endif    

}

@end

【问题讨论】:

    标签: c++ objective-c xcode macros objective-c++


    【解决方案1】:

    编译为 c++ 时定义了一个预定义的宏:__cplusplus 我猜目标 c++ 也是如此。

    【讨论】:

      【解决方案2】:

      Objective-C++ 是一种病毒式的东西,你无法真正停止。您当前的示例为代码的不同部分(C 和 C++)提供了类布局的不同视图,虽然我认为它仍然可以工作,但我很确定这不是一件好事。

      在处理与 C++ 交互的 ObjC 项目时,我通常会尽量避免在头文件中包含 C++ 引用。这使得头文件对 Objective-C 和 Objective-C++ 都有效。如果我无法避免它,那么我不会试图与之抗争(这是一个失败的原因);但我尽量不在“正常”的 ObjC 头文件中包含那个 ObjC++ 头文件,如果我需要引用该类,我会使用 @class 指令(@class SomeObjCPPClass; 而不是 #import "SomeObjCPPClass.h" 指令)。然后,我包含实现文件的头文件,它必须是 ObjC++,但至少它不会从那里传播。

      编辑 Clang 的最新版本(2012 及更高版本)允许您在 @implementation 方面声明字段。这可以有效地释放您对 C++ 的任何引用,并使 Objective-C++ 更易于管理(并且病毒更少)。要保留 OP 的示例,您现在需要:

      // SomeClass.h - a file I want to import in C++ and Objectice-C classes
      
      @interface SomeClass : BaseClass
      
      // (no fields)
      
      // (methods)
      
      @end
      

      // SomeClass.mm
      #import "CPlusPlusLibrary.h"
      
      @implementation SomeClass
      {
          CPlusPlusClass* variable;
      }
      
      // (method implementations)
      
      @end
      

      这使得 SomeClass.h 标头可以安全地包含在纯 Objective-C 文件和 Objective-C++ 文件中。

      【讨论】:

      • 我比这更进一步,并允许自己在 ObjC 头文件中将 C++ 类声明为结构,这样我就可以在 ObjC 类中包含指向它们的指针,而无需知道 ObjC 类的每个人都需要成为 ObjC++。结构和类的区别仅在于成员的默认可见性,因此调用结构的前向声明实际上是一个类没有不良副作用。
      • 谢谢zneak,您的方法对我有用,但是当我在objective-c++ 对象上调用方法时收到一些警告:Receiver 'SomeClass' 是一个转发类,对应的@interface 可能不存在跨度>
      • @phix23 你需要 #import 你的实现文件中的 ObjC++ 头文件。
      • @zneak 但它不会编译,因为我在 ObjC++ 标头中有 c++ 成员,相反我可以将 @interface 与我需要的方法复制到实现文件(省略 c++ 的东西)到摆脱警告
      • @phix23 你还需要你的实现文件是Objective-C++。如果您将扩展名更改为.mm,则一切正常。 ObjC++ 不会进一步传播,因为您的接口文件仍然是有效的 Objective-C。
      【解决方案3】:

      这将非常失败,因为 SomeClass 的实例将具有不同的大小和布局,具体取决于它是从 .m 文件还是 .mm 文件编译的。

      这就是我要做的(添加代码以使其也可包含在 .c 和 .cp 中):

      #ifdef __OBJC__
          #import <Foundation/Foundation.h>
      #endif
      
      #ifdef __cplusplus
          #include "CPlusPlusLibrary.h"
      #endif
      
      #ifdef __cplusplus
          class CPlusPlusClass;
      #else
          typedef struct CPlusPlusClass CPlusPlusClass;
      #endif
      
      #ifdef __OBJC__
          @class AnotherObjCClass;
          @interface SomeObjCClass : NSObject
          {
              CPlusPlusClass* _variableCPP;
              AnotherObjCClass* _variableObjC;
          }
      #else
          typedef struct ObjC_AnotherObjCClass AnotherObjCClass;
          typedef struct ObjC_SomeClass SomeObjCClass;
      #endif
      
      #ifdef __cplusplus
      extern "C" { // Callable from C
      #endif   
          void CFunctionTakingCPlusPlusClass(CPlusPlusClass* foo);
          void CFunctionTakingSomeObjCClass(SomeObjCClass* foo);      
      #ifdef __cplusplus
      }
      #endif
      

      请注意,在编译 .mm 文件时,__OBJC__ 和 __cplusplus 都已定义。 #include 和 #import 在 Objective-C(C-99 的超集)中大致等效,但是对于 C/C++ 可见的任何内容都必须使用 #include。

      请记住,在 C 中,从未定义过 foo 的 struct foo*(指向匿名结构的指针)是一个完全有效的类型,所以它所做的就是 typedef “其他语言类”作为匿名结构,您可以通过指针 w 传递它/o 查看内容。

      我将作为练习离开它如何与 ARC 交互以用于 ObjC 实例变量。 (可能有龙和/或您可能需要 __unsafe_unretained)

      IIRC,如果您使用与 C++ 类相同的结构名称,调试器将显示正确的信息(如果可用)。我不确定这对 Obj-C 是否安全,所以我使用了不同的名称。

      此外,尽量避免包含 C++ 标头(只需将其#include 到您的 .mm 实现文件中),除非您需要它用于除匿名类以外的类型。或者,您可以为其创建一个看起来像此文件顶部的包装头。

      HTH,-史蒂夫

      【讨论】:

      • 很好的答案。 +1 包括所有 ifdef 宏。
      • 谢谢,我正在做类似的事情,并认为值得将样板的副本留在我可以再次找到它的地方。 ;-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-01
      • 2015-11-24
      • 2010-11-08
      • 1970-01-01
      • 2013-05-17
      相关资源
      最近更新 更多