Sunday 23 February 2014

C++ and Go

I have been investigating the possibilities of using a C++ logic library I wrote some time ago for an iPad project with go to be used as part of a web site.  I knew that using C and go would be quite straight forward and in theory I could write C interfaces to my C++ and use this with Go but I wanted to see how Swig (http://www.swig.org/) would work as this now has Go support and in theory would generate all my Go and C++ interfaces for me.

Maybe I was just being thick but the documentation on the Swig site got me so far but not to working code, I also trawled the web to find a few posts and this got me a little further but nothing yet working.  After the usual couple of hours pulling my hair out and fighting with the compiler, success I managed to get everything working.

The below example was built using OSX however there is no reason this should not work on other platforms.  It also explains only how to do a native build, building for other platform straight from my desktop is one of the things I love about go, in theory there is no reason why this would not work for the Go part however the C++ library would most likely need to be built natively.

You will need to install swig which can be found here...
http://www.swig.org/

Step one: Setup folder structure To build our example we are going to need a folder for our C++ code and one for our Go code, I want to keep the C++ code outside of the GoPath as I will most likely be using Git submodules within my application.

For convenience you can clone my git repo (git@github.com:nicholasjackson/SwigGo.git) which sets out the folder structure as below:



Lets look at the C++ code first, this is a fairly straightforward class which has one public method returning "Hello World" as a char array.

helloworld.h
#ifndef HELLO_WORLD_H
#define HELLO_WORLD_H

class HelloWorld {
public:
 HelloWorld();
 ~HelloWorld();
 const char * getHelloWorld();
};

#endif


helloworld.cxx
#include "helloworld.h"

HelloWorld::HelloWorld(){}
HelloWorld::~HelloWorld(){}

const char * HelloWorld::getHelloWorld() {
 return "Hello World";
}


helloworld.swigcxx
%module helloworld
%{
    #include "helloworld.h" 
%}

%include "helloworld.h"


The swigcxx file above is the interesting one, what we are defining here is that we would like swig to generate for us.  This is the entry point to our C++ class, this will be accessible to go, we do not need to define all the headers only the one which contains public functions we would like to call.


hellotester.go
package main

import (
 "fmt"
 "helloworld"
)

func main() {
 fmt.Println("hello tester")
 world := helloworld.NewHelloWorld()
 fmt.Println(world.GetHelloWorld())
}

The above file is our Go code as you can see this calls a method on the helloworld package which is generated by Swig to return an interface which has our method returning the char array.

Step two: generate our code, thankfully this is nice and simple.

From terminal go to the cpp directory and execute the following commands:

swig -go -c++ -intgosize 64 helloworld.swigcxx

This tells swig to generate our code using Go as a destination language and with the C++ flag to tell it our input is C++ we also need to specify the size of int for go I am using 64bit.

g++ -c -fPIC helloworld.cxx
g++ -c -fPIC helloworld_wrap.cxx

Compile the C++ code if you are building your own library you can do this with your make file or build script, helloworld_wrap.cxx has been generated by Swig, in order for Go to call the methods on your library you need to add this file so that its contained in your shared object.

g++ -undefined suppress -flat_namespace -shared helloworld.o helloworld_wrap.o -o helloworld.so

Build your shared library and copy the helloworld.go, helloworld_gc.c to the go/src/helloworld, copy the helloworld.so file to the location of your go executable in this instance go/src/

The next step is to build our go code the first thing we need to do is to create a library with the go toolchain.  In terminal go to the go/helloworld folder and execute the following commands.

go tool 6c -I /usr/local/go/pkg/darwin_amd64 -D _64BIT helloworld_gc.c

This compiles the Swig generated code with the Go c compiler, the -I directive points to your build architecture, in this case OSX.

go tool 6g helloworld.go

Compile our helloworld.go file which was generated by Swig

go tool pack grc helloworld.a helloworld_gc.6 helloworld.6

Package the two files into a static library that we can access from our code.

go install

Install this package so that we can use it from our main code.
Finally all we need to do is to build our Go code, go back to the src folder and build the go code normally using:

go build -o hellotester

assuming all went well you should now have a hellotester executable in this folder and when you run it you should see:


Good luck and all source code is available at...

https://github.com/nicholasjackson/SwigGo

1 comment:

  1. I have just managed to get my logic class running with my application, so far the only change I have had to change to my application was to remove a using Namespace in a header file and reference the object direct with Namespace::Object.

    Then I had to import the templates for c++ map into my swig config like so...
    %include
    %include "std_map.i"
    namespace std {
    %template(StringMap) map;
    }

    The app builds and runs beautifully.

    ReplyDelete