Statically Link C++ Code With Go Code

Since version 2.0, SWIG can support the Go programming language. This makes it possible to write Go programs that invoke C/C++ code. Usually, the C/C++ code and the SWIG-generated C/C++ wrapper code are built into a shared library; the Go invoking code and SWIG-generated Go wrapper code are built into an executable binary, which dynamically links to the shared library.

This solution is good enough unless you are writing a distributed computing program, which will be deployed on many computers with different version of libraries installed. In the context of distributed computing, in order to avoid the complexity of deployment, we usually want to link dependent libraries statically into the binary. However, I found no way to link C/C++ code statically with Go code when I use GC compiler (5g, 6g, 8g etc), because the SWIG-generated C/C++ wrapper code are for Plan 9 C compiler (5c, 6c, 8c etc), which cannot be linked together with the C/C++ library code compiled using GCC.

However, many thanks to Ian Taylor and his GCCGO, the Go frontend for GCC, I can now link Go code with C/C++ code statically. An an example, given a C/C++ implementation of a very simple library (code from SWIG document):

/* File : example.c */

double  My_variable  = 3.0;

/* Compute factorial of n */
int  fact(int n) {
	if (n <= 1) return 1;
	else return n*fact(n-1);
}

/* Compute n mod m */
int my_mod(int n, int m) {
	return(n % m);
}

and corresponding SWIG interface file

/* File : example.i */
%module example
%{
/* Put headers and other declarations here */
extern double My_variable;
extern int    fact(int);
extern int    my_mod(int n, int m);
%}

extern double My_variable;
extern int    fact(int);
extern int    my_mod(int n, int m);

we generated C/C++ wrapper code (example_wrap.c) and Go wrapper code (example.go) using SWIG:

swig -go -gccgo example.i

By reading the generated Go code, you would know how to write the caller program. Take the following main.go as an example:

package main

import "fmt"
import "example"

func main() {
  fmt.Printf("Hello World:%d\n", example.Fact(3))
}

Let compile all these C/C++ and Go code:

gcc -c example.c -o example_impl.o
gcc -c example_wrap.c -o example_wrap.o
gccgo -c example.go -o example.o
gccgo -c main.go -o main.o

and let link then together (statically):

gccgo main.o example.o example_wrap.o example_impl.o -o hello

Here it is! Now, we can run the generated binary hello and get

Hello World!:6