【问题标题】:Interacting with C++ classes from Swift与 Swift 中的 C++ 类交互
【发布时间】:2016-05-15 17:24:28
【问题描述】:

我有一个用 C++ 编写的重要类库。我试图通过 Swift 中的某种类型的桥梁来使用它们,而不是将它们重写为 Swift 代码。主要动机是 C++ 代码代表了在多个平台上使用的核心库。实际上,我只是在创建一个基于 Swift 的 UI,以允许核心功能在 OS X 下工作。

还有其他问题在问,“我如何从 Swift 调用 C++ 函数。”这是不是我的问题。要桥接到 C++ 函数,以下方法可以正常工作:

通过“C”定义桥接头

#ifndef ImageReader_hpp
#define ImageReader_hpp

#ifdef __cplusplus
extern "C" {
#endif

    const char *hexdump(char *filename);
    const char *imageType(char *filename);

#ifdef __cplusplus
}
#endif

#endif /* ImageReader_hpp */

Swift 代码现在可以直接调用函数

let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))

我的问题更具体。如何在 Swift 中实例化和操作 C++ 类?我似乎找不到任何关于此的内容。

【问题讨论】:

  • 我个人求助于编写简单的 Objective-C++ 包装文件,这些文件公开了一个 Objective-C 类,该类重现所有相关的 C++ 调用,并将它们简单地转发到 C++ 类的持有实例。在我的例子中,C++ 类和调用的数量很少,所以不是特别耗费人力。但我会暂缓将其作为答案,希望有人能提出更好的建议。
  • 嗯,这有点……让我们拭目以待(并希望)。
  • 我收到了一个通过 IRC 编写的 Swift 包装类的建议,该类维护一个指向实际 C++ 对象的 void 指针并公开所需的方法,这些方法实际上只是通过 C 桥和指向对象的指针。
  • 作为对此的更新,Swift 3.0 现已发布,尽管之前做出了承诺,但 C++ 互操作性现在被标记为“超出范围”。

标签: c++ swift


【解决方案1】:

除了您自己的解决方案之外,还有另一种方法可以做到。可以在objective-c++中调用或直接编写C++代码。

因此,您可以在 C++ 代码之上创建一个 Objective-C++ 包装器并创建一个合适的接口。

然后从您的 swift 代码中调用 Objective-C++ 代码。为了能够编写 Objective-C++ 代码,您可能必须将文件扩展名从 .m 重命名为 .mm

不要忘记在合适的时候释放 C++ 对象分配的内存。

【讨论】:

    【解决方案2】:

    您可以使用Scapix Language Bridge 自动将 C++ 桥接到 Swift(以及其他语言)。桥接代码直接从 C++ 头文件即时自动生成。这是example

    C++:

    #include <scapix/bridge/object.h>
    
    class contact : public scapix::bridge::object<contact>
    {
    public:
        std::string name();
        void send_message(const std::string& msg, std::shared_ptr<contact> from);
        void add_tags(const std::vector<std::string>& tags);
        void add_friends(std::vector<std::shared_ptr<contact>> friends);
    };
    

    斯威夫特:

    class ViewController: UIViewController {
        func send(friend: Contact) {
            let c = Contact()
    
            contact.sendMessage("Hello", friend)
            contact.addTags(["a","b","c"])
            contact.addFriends([friend])
        }
    }
    

    【讨论】:

    • 有趣。看来您是在 2019 年 3 月才首次发布此内容。
    • @boris-rasin 在示例中我找不到直接的 c++ 到 swift bridge。您的计划中有此功能吗?
    • 是的,目前没有直接的 C++ 到 Swift 的桥。但是 C++ 到 ObjC 的桥非常适合 Swift(通过 Apple 的 ObjC 到 Swift 的桥)。我现在正在研究直接桥接(在某些情况下它会提供更好的性能)。
    • 哦,是的,你完全正确,但在 linux 上的裸 swift 项目中,情况并非如此。期待看到您的实施。
    • @boris-rasin 我们正在考虑将一个大型 C++ 库整合到我们的 SDK 中。通过在 scapix::bridge::object 上重新定义每个类来修改库本身是不可行的。我们无法控制该来源,每次更新的维护都将是一场噩梦。此外,如果我没记错的话,每个开发人员和构建系统都需要将 Scapix 集成到工具链中?
    【解决方案3】:

    正如另一个答案提到的,使用 ObjC++ 进行交互要容易得多。只需将文件命名为 .mm 而不是 .m 和 xcode/clang,即可让您访问该文件中的 c++。

    请注意,ObjC++ 不支持 C++ 继承。我想在 ObjC++ 中继承一个 c++ 类,但你不能。您必须用 C++ 编写子类并将其包装在 ObjC++ 类中。

    然后使用通常用于从 swift 调用 objc 的桥接头。

    【讨论】:

    • 您可能错过了重点。这些接口是必需的,因为我们有一个非常大的、多平台的 C++ 应用程序,我们现在也在 OS X 上支持它。 Objective C++ 确实不是整个项目的选择。
    • 是的,我回答了你的问题。您似乎希望从 swift 中直接操作 C++。我不知道该怎么做。
    • 这就是我发布的答案所完成的。 C 函数允许您将对象作为任意内存块传入和传出,并在任一侧调用方法。
    • @DavidHoelzer 它认为这很酷,但你不是这样让代码更复杂吗?我想这是主观的。 ObjC++ 包装器对我来说似乎更干净。
    • 但这并不是 Swift 真正的正当借口。 Swift 只需要关心苹果风格的 Clang 使用的名称修饰。
    【解决方案4】:

    我想出了一个完全可以管理的答案。您希望它有多干净完全取决于您愿意做多少工作。

    首先,获取您的 C++ 类并创建 C“包装器”函数以与其交互。例如,如果我们有这个 C++ 类:

    class MBR {
        std::string filename;
    
    public:
        MBR (std::string filename);
        const char *hexdump();
        const char *imageType();
        const char *bootCode();
        const char *partitions();
    private:
        bool readFile(unsigned char *buffer, const unsigned int length);
    };
    

    然后我们实现这些 C++ 函数:

    #include "MBR.hpp"
    
    using namespace std;
    const void * initialize(char *filename)
    {
        MBR *mbr = new MBR(filename);
    
        return (void *)mbr;
    }
    
    const char *hexdump(const void *object)
    {
        MBR *mbr;
        static char retval[2048];
    
        mbr = (MBR *)object;
        strcpy(retval, mbr -> hexdump());
        return retval;
    }
    
    const char *imageType(const void *object)
    {
        MBR *mbr;
        static char retval[256];
    
        mbr = (MBR *)object;
        strcpy(retval, mbr -> imageType());
        return retval;
    }
    

    然后桥头包含:

    #ifndef ImageReader_hpp
    #define ImageReader_hpp
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
        const void *initialize(char *filename);
        const char *hexdump(const void *object);
        const char *imageType(const void *object);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* ImageReader_hpp */
    

    在 Swift 中,我们现在可以实例化对象并与它进行交互,如下所示:

    let cppObject = UnsafeMutablePointer<Void>(initialize(filename))
    let type = String.fromCString(imageType(cppObject))
    let dump = String.fromCString(hexdump(cppObject))                
    self.imageTypeLabel.stringValue = type!
    self.dumpDisplay.stringValue = dump!
    

    因此,如您所见,解决方案(实际上相当简单)是创建包装器,该包装器将实例化一个对象并返回一个指向该对象的指针。然后可以将其传递回包装函数,这些函数可以轻松地将其视为符合该类的对象并调用成员函数。

    让它更干净

    虽然这是一个美妙的开始,并证明使用现有的 C++ 类和一个简单的桥是完全可行的,但它可以更简洁。

    清理这个只是意味着我们从我们的 Swift 代码中间删除 UnsafeMutablePointer&lt;Void&gt; 并将其封装到一个 Swift 类中。本质上,我们使用相同的 C/C++ 包装函数,但将它们与 Swift 类接口。 Swift 类维护对象引用,本质上只是将所有方法和属性引用调用通过桥传递给 C++ 对象!

    完成此操作后,所有的桥接代码都完全封装在 Swift 类中。即使我们仍在使用 C 桥接器,我们也可以透明地有效地使用 C++ 对象,而无需在 Objective-C 或 Objective-C++ 中重新编码。

    【讨论】:

    • 这里有几个重要的遗漏问题。第一个是析构函数永远不会被调用,并且对象被泄露了。第二个是异常可能会导致一些令人讨厌的麻烦。
    • 我在回答这个问题 stackoverflow.com/questions/2045774/… 时涵盖了很多问题
    • @DavidHoelzer,我只是想强调这个想法,因为一些开发人员会尝试包装整个 C++ 库并使用它,因为它是用 Swift 编写的。您提供了一个很好的示例,说明如何“在 Swift 中实例化和操作 C++ 类”!我只是想补充一点,应该谨慎使用这种技术!谢谢你的榜样!
    • 不要认为这是“完美的”。我认为这几乎与您编写一个 Objective-C 包装器然后在 Swift 中使用它一样。
    • @Bagusflyer 肯定...除了它根本不是一个 Objective-C 包装器。
    【解决方案5】:

    Swift 目前没有 C++ 互操作。这是一个长期目标,但在不久的将来不太可能实现。

    【讨论】:

    • 将“使用 C 包装器”称为“C++ 互操作”的形式有点过于夸张了
    • 也许,但是使用它们来允许您实例化一个 C++ 类,然后在其上调用方法使其有用并满足问题。
    • 事实上,这不再是一个长期目标,而是在路线图中被标记为“超出范围”。
    • 使用 c-wrappers 是几乎所有 C++ 与任何语言、python、java 等互操作的标准方法。
    • 这是因为 C++ 名称修改依赖于编译器,对吧?
    猜你喜欢
    • 1970-01-01
    • 2013-03-29
    • 1970-01-01
    • 1970-01-01
    • 2011-09-17
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多