Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interacting with C++ classes from Swift

Tags:

c++

swift

I have a significant library of classes written in C++. I'm trying to make use of them through some type of bridge within Swift rather than rewrite them as Swift code. The primary motivation is that the C++ code represents a core library that is used on multiple platforms. Effectively, I'm just creating a Swift based UI to allow the core functionality to work under OS X.

There are other questions asking, "How do I call a C++ function from Swift." This is not my question. To bridge to a C++ function, the following works fine:

Define a bridging header through "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 code can now call functions directly

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

My question is more specific. How can I instantiate and manipulate a C++ Class from within Swift? I can't seem to find anything published on this.

like image 662
David Hoelzer Avatar asked Feb 05 '16 16:02

David Hoelzer


People also ask

Can you call C from Swift?

In Swift, you can call C variadic functions, such as vasprintf(_:_:_:) , using the Swift getVaList(_:) or withVaList(_:_:) functions.

Can you mix Objective-C and Swift?

You can use Objective-C and Swift files together in a single project, no matter which language the project used originally. This makes creating mixed-language app and framework targets as straightforward as creating an app or framework target written in a single language.


1 Answers

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++.

like image 120
David Hoelzer Avatar answered Sep 22 '22 11:09

David Hoelzer