Calling C++ from Golang
Calling C++ from Golang
Golang (Go) is an interesting language. From a Java perspective Go is low-level, fast with a tiny footprint. From a C-perspective Go feels like a sandbox environment: safe (C/C++ fanatics might say boring) and lacking in power.
Go has found its place as a low-footprint alternative for Spring Boot in some of my projects. Another use case for Go is interfacing with a C/C++ library. Of course this is possible with Java using JNI but it’s just a lot easier in Go: Java lives a continent away from C, while Go is more like its friendly neighbour.
Calling C from Go
Calling C from Go is simple. You import the “C” package and call C functions. You can even “mix” C with Go, which will be sorted out by cgo:
package main
//#include <math.h>
//double number() {
// return 1.5;
//}
import "C"
func main() {
num1 := C.number()
num2 := C.double(5)
println("The number is", C.fmax(num1, num2))
}
Output:
The number is +5.000000e+000
Calling C++ from Go
Is not possible…
At least, is not possible to directly instantiate and operate on C++ classes. The solution? Wrapping the C++ classes in C functions, which can then be called by Go. Let’s say we have a C++ library ‘libcustom’ containing a class ‘MyClass’ with a ‘hello’ method we want to call:
package main
//#cgo LDFLAGS: -lcustom
//#include <custom/MyClass.h>
//void* myclass_new() {
// return new MyClass();
//}
//void myclass_hello(void* handle) {
// reinterpret_cast<MyClass*>(handle)->hello();
//}
import "C"
func main() {
handle := C.myclass_new()
C.myclass_hello(handle)
}
// (Forgive my crappy, rusty C++ skills...)
Things to note in the example above:
- We create a C-wrapper for every C++ method we want to call.
- We tell cgo that ‘libcustom’ is required when linking the application (with the ‘#cgo’ pragma).
- If you or the library is using any standard C++ functionality, you’ll probably have to link ‘libstdc++’ too.
- You probably noticed I forgot to properly destruct the instantiated object, causing a memory leak. I’m leaving it as an exercise for you to fix it :-).
The hard stuff
There are a few things that took me a while to figure out, here are some of them:
Pointer-to-Pointer
One of the functions I needed to call takes a pointer-to-pointer parameter (a bit like an out parameter in C#). This is something I just couldn’t get to work from Go. My solution: handling the pointer-to-pointer in C and function result struct returning a pointer.
int doSomething(HANDLE h, unsigned char **output, unsigned int *length);
became:
typedef struct SomethingResult {
unsigned char *output;
unsigned int length;
} SomethingResult;
SomethingResult doSomething(HANDLE h);
Update: after some more searching this turned pretty basic Pointer 101:
var output *C.uchar
var len C.uint
doSomething(h, &output, &len)
However: with a construction like this you know that doSomething is allocating memory for output, which must be freed at some point to avoid memory leaks (remember: C is not garbage collected). Doh!
Passing a Go byte array to C
Luckily Go and C agree on how a sequence of bytes should be represented in memory: simply a sequence of 8-bit numbers. This makes passing a byte array pretty easy. The C-library in question represents a byte as an unsigned char, so some obscure casting is required:
(*C.uchar)(unsafe.Pointer(&gostr[0]))
Linker error messages
The error messages the linker (‘ld’) is providing weren’t very helpful. It took me half a day to find out why it kept saying ‘unable to find -lsomething’: is the search path (specified with ‘-L’) incorrect? Is the library corrupt? Did I specify it in the wrong way? Do I need to give it some specific extension? It turned out libraries must be prefixed with ‘lib’, so your filename should be ‘libsomething.so’ (.so for my specific Linux distro) and the linker flag ‘-llibsomething.so’.
What about SWIG?
An alternative route would be using SWIG (Simplified Wrapper and Interface Generator). SWIG is a tool to generate wrappers for C/C++ libraries for a pletora of languages, including Go. What SWIG generates is basically the same as we build our own above. I wasn’t able to compile the wrappers generated by SWIG, but then again that’s undoubtly due to my rusty C/C++ skills. If you’re comfortable using SWIG, then by all means use it. In that case you probably wouldn’t reading this article, anyways.
Epilogue: why do they call it ‘unsafe’?
Normally your application’s memory is safely managed by Go: array bounds checking, garbage collection, things that make Go safe and different from C. Now, when you’re passing a pointer to a piece of Go’s memory to code which is not Go (in our case C), you throw all that safety out of the window. For example: when you pass a pointer to a byte array to C and it decides to write beyond the actual array bounds (because you passed the wrong length or the C code is buggy), who knows what will happen? You don’t know what Go stored in memory next to the byte array you passed. Your program might crash or even worse, its state changed without you noticing. Not good when you’re writing mission-critical stuff.
//void corruptMemory() {
// int *number = 0xc000042780; // Taken from the println below
// *number = 1337;
//}
import "C"
func main() {
number := 0
println("Memory address is:", &number)
println("Before =", number)
C.corruptMemory()
println("After =", number)
}
Outputs:
Memory address is: 0xc000042780
Before = 0
After = 1337
We didn’t pass our variable, but it changed value nevertheless. Interfacing with native code outside Go’s influence is certainly powerful, but needs to be used with extreme care.