99

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.

David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
  • 11
    I have personally resorted to writing plain Objective-C++ wrapper files that expose an Objective-C class which reproduces all of the relevant C++ calls and simply forwards them to a held instance of the C++ class. In my case the number of C++ classes and calls is small so it's not particularly labour intensive. But I'll hold off on advocating this as an answer in the hope that someone will have come up with something better. – Tommy Feb 05 '16 at 16:45
  • 1
    Well, it's something... Let's wait and see (and hope). – David Hoelzer Feb 05 '16 at 16:51
  • I've received a suggestion via IRC to write a Swift wrapper class that maintains a void pointer to the actual C++ object and exposes required methods which are, effectively, just passed through via the C bridge and the pointer to the object. – David Hoelzer Feb 05 '16 at 17:15
  • 5
    As an update to this, Swift 3.0 is now out and, despite previous promises, C++ interoperability is now marked as "Out of scope." – David Hoelzer Jul 16 '16 at 09:04

5 Answers5

71

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

David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
  • 16
    There's a couple of important missed issues here. The first being that the destructor is never called, and the object is leaked. The second is that exceptions could cause some nasty trouble. – Michael Anderson Feb 18 '16 at 07:59
  • 2
    I covered a bunch of the issues in my answer to this question http://stackoverflow.com/questions/2045774/developing-c-wrapper-api-for-object-oriented-c-code/2045860 – Michael Anderson Feb 18 '16 at 08:02
  • Wrapping in ObjC++ is the better method. Memory is better handled and you don't need to hold and pass around a (void*). Consider using a tool like Djinni to create the Obj-C interfaces for you. – Josh Knauer Jan 05 '18 at 15:38
  • It is great that it is possible to integrate components written in these two languages. An important step is to review the design of your application and minimize the labour work necessary to put them together. You could develop a part of your system as independent component written in C++ and provide a minimal interface exposed to the Swift code. – Nicolai Nita Mar 21 '18 at 11:56
  • That's the entire purpose of this, @NicolaiNita – David Hoelzer Mar 21 '18 at 13:27
  • 2
    @DavidHoelzer, I just wanted to stress this idea because some developers will try to wrap an entire C++ library and use it as it was written in Swift. You gave a great example of how to "instantiate and manipulate a C++ Class from within Swift"! And I just wanted to add that this technique should be used with cautious! Thank you for your example! – Nicolai Nita Mar 21 '18 at 16:18
  • 1
    Don't think this is "perfect". I think it's almost the same as you write an Objective-C wrapper then use it in Swift. – Bagusflyer Jan 05 '20 at 03:12
  • 1
    @Bagusflyer sure... except that it's not an objective-C wrapper at all. – David Hoelzer Jan 05 '20 at 11:42
13

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

Catfish_Man
  • 41,261
  • 11
  • 67
  • 84
9

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.

Ahmed
  • 14,503
  • 22
  • 92
  • 150
8

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])
    }
}
Boris Rasin
  • 448
  • 4
  • 12
  • Interesting. Looks like you just released this for the first time in March of 2019. – David Hoelzer Mar 27 '19 at 17:08
  • @boris-rasin I can't find direct c++ to swift bridge in the example. Do you have this feature in plans? – sage444 Mar 29 '19 at 09:02
  • 2
    Yes, there is no direct C++ to Swift bridge at the moment. But C++ to ObjC bridge works for Swift perfectly fine (through Apple's ObjC to Swift bridge). I am working on the direct bridge right now (it will provide better performance in some cases). – Boris Rasin Mar 29 '19 at 10:56
  • 1
    Oh yeah, you are totally right, but in bare swift project on linux this is not the case. Looking forward to look at your implementation. – sage444 Mar 29 '19 at 12:40
  • @boris-rasin We are looking at incorporating a large C++ library into our SDK. Modifying the library itself by rebasing every class on scapix::bridge::object<> isn't feasible. We don't control that source and the maintenance for every update would be a nightmare. In addition, every developer and build system would need Scapix integrated into the toolchain, if I'm not mistaken? – David Gish Mar 12 '21 at 18:26
  • @DavidGish Integration is optional: [Scapix optional integration](https://www.scapix.com/language_bridge/optional_integration/) – Boris Rasin Sep 04 '22 at 08:53
6

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.

nnrales
  • 1,481
  • 1
  • 17
  • 26
  • 2
    You may have missed the point. The interfaces is required because we have a very large, multi-platform C++ application that we are now supporting on OS X as well. Objective C++ really isn't an option for the entire project. – David Hoelzer Mar 21 '18 at 13:28
  • I am not sure I understand you. You just need the ObjC++ when you want a call to go between swift and c++ or vice versa. Just the boundaries need to be written in objc++, not the whole project. – nnrales Mar 21 '18 at 22:06
  • 1
    Yeah, I went over your question. You seem to be looking to directly manipulate C++ from swift. I don't know how to do that. – nnrales Mar 21 '18 at 22:09
  • 1
    That's what my posted answer accomplishes. The C functions allow you to pass the objects in and out as arbitrary hunks of memory and to call methods on either side. – David Hoelzer Mar 21 '18 at 22:37
  • 1
    @DavidHoelzer It think it is cool, but aren't you making the code more complicated this way ? I guess it is subjective. The ObjC++ wrapper, seems cleaner to me. – nnrales Mar 21 '18 at 22:40
  • There’s little choice to interop C++ with other languages other than C because C++ name mangling isn’t fully-standardized across platforms and most runtimes don’t want to support multiple APIs. Plus, it’s trivial to wrap a C API as a C++ one. –  Jun 08 '18 at 19:05
  • 2
    That's not really a valid excuse for Swift though. Swift only needs to concern itself with the name mangling that the Apple-flavour of Clang uses. – James Jan 08 '19 at 15:14