与 Swift 中的 C + + 类交互

我有一个很重要的用 C + + 编写的类库。我试图通过 Swift 中的某种桥梁来使用它们,而不是将它们重写为 Swift 代码。主要动机是 C + + 代码表示一个在多个平台上使用的核心库。实际上,我只是创建了一个基于 Swift 的用户界面,允许核心功能在 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 + + 类?我找不到任何关于这个的文章。

61196 次浏览

Swift has no C++ interop currently. It's a long-term goal, but is very unlikely to happen in the near future.

I've worked out a perfectly manageable answer. How clean you'd like this to be is entirely based upon how much work you're willing to do.

First, take your C++ class and create C "wrapper" functions to interface with it. For example, if we have this C++ class:

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);
};

We then implement these C++ functions:

#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;
}

The bridge header then contains:

#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 */

From Swift, we can now instantiate the object and interact with it like so:

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!

So, as you can see, the solution (which is actually rather simple) is to create wrappers that will instantiate an object and return a pointer to that object. This can then be passed back into the wrapper functions which can easily treat it as an object conforming to that class and call the member functions.

Making It Cleaner

While this is a fantastic start and proves that it is completely feasible to use existing C++ classes with a trivial bridge, it can be even cleaner.

Cleaning this up would simply mean that we remove the UnsafeMutablePointer<Void> from the middle of our Swift code and encapsulate it into a Swift class. Essentially, we use the same C/C++ wrapper functions but interface them with a Swift class. The Swift class maintains the object reference and essentially just passes all method and attribute reference calls through the bridge to the C++ object!

Having done this, all of the bridging code is completely encapsulated in the Swift class. Even though we are still using a C bridge, we are effectively using C++ objects transparently without having to resort to recoding them in Objective-C or Objective-C++.

In addition to your own solution, there is another way to do it. You can call or directly write C++ code in objective-c++.

So you can create an objective-C++ wrapper on top of your C++ code and create a suitable interface.

Then call objective-C++ code from your swift code. To be able to write objective-C++ code you may have to rename file extension from .m to .mm

Do not forget to release memory allocated by your C++ objects when suitable.

As another answer mentioned, using ObjC++ to interact is much easier. Just name your files .mm instead of .m and xcode/clang, gives you access to c++ in that file.

Note that ObjC++ does not support C++ inheritance. I you want to subclass a c++ class in ObjC++, you can't. You will have to write the subclass in C++ and wrap it around an ObjC++ class.

Then use the bridging header you would normally use to call objc from swift.

You can use Scapix Language Bridge to automatically bridge C++ to Swift (among other languages). Bridge code automatically generated on the fly directly from C++ header files. Here is an 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);
};

Swift:

class ViewController: UIViewController {
func send(friend: Contact) {
let c = Contact()


contact.sendMessage("Hello", friend)
contact.addTags(["a","b","c"])
contact.addFriends([friend])
}
}