我可以在编译和链接时将 C++ 主函数和类与 Objective-C 和/或 C 例程分开吗?

问题描述 投票:0回答:2

我有一个小型 C++ 应用程序,我导入了 Objective-C 类。 它以 Objective-C++ 文件 .mm 的形式工作,但任何包含头文件(最终可能包含一些 Objective-C 头文件)的 C++ 文件都必须重命名为 .mm 扩展名,以适应正确的 GCC 驱动程序。

有没有办法为 Objective-C 类编写一个纯 C++ 包装器,或者我可以以某种方式将 Objective-C 对象分离出来并单独链接它们吗? 也许即使 Objective-C 类成为一个小库,我也可以在编译时静态地重新链接?

问题在于这段代码是跨平台的,并且在通常不使用 Objective-C 的系统(即非 Mac)上编译起来更加困难。 即使预处理器命令限制 Objective-C 代码在 Windows 或 Linux 上的任何实现,原始代码仍然具有 .mm 扩展名,并且 GCC 仍然将代码视为 Objective-C++。

c++ objective-c objective-c++
2个回答
3
投票

通常你只需用 C++ 类包装你的 Objective-C 类,例如:使用不透明指针并将对 C++ 方法的调用转发给 Objective-C 方法。

这样,您的可移植 C++ 源代码永远不必看到任何 Objective-C 包含内容,理想情况下您只需更换不同平台上包装器的实现文件即可。

示例:

// c++ header:
class Wrapper {
    struct Opaque;
    Opaque* opaque;
    // ...
public:
    void f();
};

// Objective-C++ source on Mac:
struct Wrapper::Opaque {
    id contained;
    // ...
};

void Wrapper::f() {
    [opaque->contained f];
}

// ...

3
投票

是的,如果您知道一些技巧,这两种方式都很容易做到:

1) “id”类型实际上是在普通的 C 头文件中定义的。所以你可以执行以下操作:

在你的标题中:

#include <objc/objc.h>

class MyWindow
{
public:
    MyWindow();
    ~MyWindow();
protected:
    id        mCocoaWindow;
};

在您的实施中(.mm):

#include "MyWindow.h"
#include <Cocoa/Cocoa.h>

MyWindow::MyWindow()
{
    mCocoaWindow = [[NSWindow alloc] init];
}

MyWindow::~MyWindow()
{
    [mCocoaWindow release];
    mCocoaWindow = nil;
}

2) 当源文件包含两个预处理器常量(但不是 ObjC++ 之一)时,您可以使用两个预处理器常量来排除 C++/ObjC 特定代码:

#if __OBJC__
// ObjC code goes here.
#endif /* __OBJC__*/

#if __cplusplus
// C++ code goes here.
#endif

请小心,您不能仅使用 #ifdef 添加/删除 ivars 或虚拟方法,这将创建两个具有不同内存布局的类,并使您的应用程序以非常奇怪的方式崩溃。

3)您可以使用指向结构的指针而不声明其内容:

在你的标题中:

@interface MyCppObjectWrapper : NSObject
{
    struct MyCppObjectWrapperIVars    *ivars;    // This is straight ObjC, no ++.
}

@end

在您的实施文件 (.mm) 中:

struct MyCppObjectWrapperIVars
{
    std::string    myCppString1;
    std::string    myCppString2;
    std::string    myCppString3;
};

@implementation MyCppObjectWrapper

-(id)  init
{
    if(( self = [super init] ))
    {
        ivars = new MyCppObjectWrapperIVars;
    }

    return self;
}

-(void) dealloc
{
    delete ivars;
    ivars = NULL;

    [super dealloc];
}

@end

这将使您的标头成为简单的标准 C 或 ObjC,而您的实现文件将获取所有调用的 ivars 的构造函数/析构函数,而无需将每个 ivars 作为堆上的对象创建/删除。

这只是 Mac 方面的事情,但这意味着您可以将 ObjC 内容保留在标头之外,或者至少在从 C++ 可移植层的 Mac 实现的跨平台客户端文件中使用它时进行编译.

© www.soinside.com 2019 - 2024. All rights reserved.