How to import a C++ header & use its library?

Looking through the documentation, I see mention of the ability to use C++ from Swift, but not many examples beyond the simple case of using the std C++ library with "import CxxStdlib".

I haven't found a robust example even of a Hello World program that uses C++ so far. So I must ask, if I want to import some random C++ library's header and link with that library, how is that done?

I tried specifying a module map, like the documentation suggests, and swiftc 5.9 just ignored it. I did an import Foo and provided a -I to the correct header directory, and swiftc gave an error on the line "import Foo".

3 Likes

I would also love to know an answer to this!

1 Like

So right now my most accessible example is the swift cmake examples; https://github.com/apple/swift-cmake-examples/tree/main/3_bidirectional_cxx_interop

This project demonstrates bi-directional swift c++ interop. The most important parts are probably getting the module map set up, setting the module search path with -I, and telling the compiler to import headers in C++ mode with-cxx-interoperability-mode=default. Let me know if you want more. If I weren’t on a phone at the moment, I would type out a little example as a makefile with the exact commands.

3 Likes

Not guaranteed to always work but I do try to keep it updated, this unofficial cxx interop test repo, it has bidirectional circular interop: GitHub - plotfi/cxx-interop-test: Small test app for C++ Interop with Swift.

For a more real-world program example, I used the above example repo (mainly for the CMake and module map) as a template for writing a tiny http server that uses bidirectional C++-Interop along with Swift's Async+Await:

Network code is C and C++, most of the http proto handling code for get requests is C++, minetypes are handled by a call back to swift using reverse interop (passed std::strings), a std::vector is passed from Swift to C++ that contains a socket and a request ID. Concurrency is handled by Swift.

I hope these are sufficient CMake examples.

1 Like

Both of these examples are for C++ which is in the local directory.
What about if I want to use C++ code that is in e.g. /usr/local/include & /usr/local/lib?

3 Likes

I believe no matter what libraries in /usr/local etc you want to use are, they have to be modularized first. @Alex_L What do you think?

Yep. The headers need to be in a module map file for swift to import it. That work is done for you in the Darwin SDKs, not so much elsewhere. Sometimes you’ll want to use vfs-overlays to inject the module map into the right locations, like what was done with the windows swift stuff, to enable importing the windows SDK headers without needing to change the VisualStudio itself.

1 Like

I tried to hook up Magick++ into Swift with C++-Interop, but ran into modularity issues. For instance, if proper nesting isn't enforced then the library isn't modular ( as per ⚙ D11844 [Modules] More descriptive diagnostics for misplaced import directive), you'll get something like: error: import of module 'Darwin.C.float' appears within namespace 'MagickCore'

I ran into this due to code that resembles the following:

//
// Include ImageMagick headers into namespace "MagickCore". If
// MAGICKCORE_IMPLEMENTATION is defined, include ImageMagick development
// headers.  This scheme minimizes the possibility of conflict with
// user code.
//
namespace MagickCore
{
#include <MagickCore/MagickCore.h>
#include <MagickWand/MagickWand.h>
#undef inline // Remove possible definition from config.h

#undef class
}

I know for certain that lots of non-trivial and robust Darwin SDK's work with interop that are not in the local dir either. They may not be C++ (except that compiling with C++ does enable C++-isms relating to extern "C" and enum behavior etc).

I've put together a pretty succinct example using an external library for you at:

This is a small Swift hello world program that uses the box2d physics engine, I ported it from box2d's own hello world unit test.

2 Likes

It's still not quite working for me. It seems swiftc is creating a "public enum" of the namespace and it expects that all classes within the namespace will be values of that enum. But my class isn't.

error: 'TheCppClass' is not a member type of enum '__ObjC.TheCppModule'

I downloaded your code and had a try, it seems that you avoided using c++ class because all the types I see in the swift code are c++ struct.

For example,

var groundBody = world.CreateBody(&groundBodyDef)

Could you explicitly set the type for groundBody? in c++, it is:

b2Body* CreateBody(const b2BodyDef* def);

b2Body is a c++ class, when I tested the code, the class is not exported so swift can't use it.

Could you have a look at it, please?

@plotfi I was wrong and your project works.

I ignored your b2_body.h.patch code.

Actually it's key modification that makes the project working.

Thanks!