[disclaimer]


This is a personal blog. The opinions expressed here represent my own and not those of any of my employers or customers.

Except if stated otherwise, all the code shared is reusable under a MIT/X11 licence. If a picture is missing a copyright notice, it's probably because I'm owning it.

Thursday, January 24, 2013

C++ bindings for monotouch using SWIG

cold cold light
(c) S. Delcroix 2013
I love bindings. I've always loved them. Back in the days, I was binding gtk+ and other gobject libs to C# for fun and f-spot usage. Then I bound some obj-C libs to monotouch for a client and some for pleasure.

But last week I faced something new. I wanted to bind (for monotouch) a C++ iPhone lib for which I only received the binaries and the headers files. The component was too large to even think about doing a manual C glue code. I googled about the possible solutions and the only valuable advice was to use SWIG, without any rationale or tutorial. This is then probably a first. An explanation on why SWIG can help you for this, the problem I ran into and the solutions I found.



Mono.Cxxi is sexy, but not all-purpose

I'm not the kind of guy who blindly follows recommendations, so my first try was to use anything but SWIG. I opted for Mono.Cxxi and created a full binding in a few hours. It was really easy after I figured out the gcc-xml installation steps and caveats with clang. But when I first tried to use inside my monotouch project, it was to no avail. A quick reality-check confirmed my fears:


That being said, Mono.Cxxi is great stuff, check it out. But don't count on it for static AOT.

Go SWIG

Miguel was, once again right. SWIG was the only sensible option. I've used SWIG in the 90's for python (iirc), and it looks like the website hasn't left this era. But that's for the surface only. The tool is quite capable, is well suited for .NET and you can customise it to your liking. There's even a tutorial so I won't cover the basics. 
pro-tip: on Mac, install swig with homebrew

 1. Get the DllImports right

The first thing to get right, is the DllImportAttribute for P/Invoke. It has to be like this
[DllImport ("__Internal")]
public static extern void hello ();
You can instruct swig to do that by passing the -dllimport option
swig -c++ -csharp -dllimport __Internal

2.  Generate an obj-c++ wrapper

Swig generates glue code for you in a .cxx file. It turns out that, by simply renaming it to .mm, you get a perfectly valid Obj-C++ file. Create a new (Foo_wrapper) library project with Xcode, link both that .mm file and the includes of the original library, and you'll easily get an iPhone-suited wrapper.

3. Getting rid of the AssemblyLoadExceptions

As is, the generated code compiles with smcs but crashes with an AssemblyLoadException as soon as you try to run it on the device or the simulator. That's because some SWIG generated helpers use reverse callbacks and those callbacks are usually JIT'ed by mono, which is not possible with static AOT. The solution for this is then to tag the (autogenerated) SetPending*Exception() and CreateString() with [MonoPInvokeCallback]. I do that by patching the file after the SWIG step.

4. Putting all the pieces together

So here's a (simplified) Makefile that you can use to turn your SWIG foo.i interface file to a Foo.dll assembly wrapping and embedding your original libFoo.a and the generated libFoo_wrapper.dll

5. One last thing, set IsCxx = true

Don't forget to get your AssemblyInfo.cs right, i.e. setting IsCxx to true in your LinkWithAttribute.


Wrapping up

At this point, you should be all set and you should be able to use your native c++ library directly from within monotouch. Now you have to use your brain, mess with your foo.i file and .NET-ify a bit your API.

Would you have any issue with this, or any other binding or mobile development related stuff, contact me, I'm available for contracting.


2 comments:

  1. Thanks, this was useful.

    Instead of manually patching, I created this little ruby script to do it for me:

    https://gist.github.com/banshee/7000449

    Run with:

    ruby nameOfWhateverYouSaveThisAs.rb fooPINVOKE.cs

    ReplyDelete
  2. Really liked the article and it help me immensely to get things off the ground, but I can't get the linking right, Xamarin Studio isn't getting the second library. Did you put together a project for this?

    Thanks.

    ReplyDelete