与Swift中的C ++类进行交互

时间:2016-02-05 16:37:54

标签: c++ swift

我有一个用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 ++类?我似乎无法在此发现任何内容。

5 个答案:

答案 0 :(得分:46)

我找到了一个完全可控的答案。你想要的多么干净完全取决于你愿意做多少工作。

首先,使用您的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<Void>并将其封装到Swift类中。本质上,我们使用相同的C / C ++包装器函数,但将它们与Swift类接口。 Swift类维护对象引用,基本上只是将所有方法和属性引用调用通过桥传递给C ++对象!

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

答案 1 :(得分:9)

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

答案 2 :(得分:5)

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

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

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

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

答案 3 :(得分:4)

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

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

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

答案 4 :(得分:3)

您可以使用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])
    }
}