Two Ways to Call C Functions in Go Using CGO: Static Compilation and Dynamic Linking
Nice technique, learn one!
In Go, you can call C functions through CGO. The simplest way is to write the C function directly in the line before import "C":
package main
/*
int sum(int a, int b) { return a + b; }
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.sum(C.int(1), C.int(2)))
}
But, if it’s such a simple function, there’s no need to use CGO; you can implement it directly in Go. If you really need to use CGO, it’s probably because you need to call some more complex C code. You can implement it in the following two ways:
Static Compilation
The above example is actually static compilation; the compiler will compile the C code together with the Go code into the executable file.
As mentioned earlier, in actual project development, calling C functions won’t be that simple. Generally, there will be one or more .c files. How does Go call them? It’s actually not difficult; you just need to include the corresponding header file in Go (if there isn’t one, you need to define one yourself):
Continuing with the sum() example above, first we need a sum.h:
int sum(int a, int b);
Then we need sum.c:
#include "sum.h"
int sum(int a, int b)
{
return a + b;
}
Put sum.h and sum.c in the same directory as the Go code file that calls it, then in the Go code, just:
package main
/*
#include "sum.h"
*/
import "C"
import "fmt"
func main() {
fmt.Println(C.sum(C.int(1), C.int(2)))
}
Execute CGO_ENABLED=1 go build -o cgo_example . to statically compile the C code into the executable program.
Of course, the above writing may not be elegant. You can put the CGO-related C and Go code into a directory (for example /cgo), and then encapsulate the entry:
package cgo
/*
#include "sum.h"
*/
import "C"
func Sum(a, b int) int {
return int(C.sum(C.int(a), C.int(b)))
}
Dynamic Linking
Static linking has a requirement that you must have the C code. If you only have the header file but no C code, or it’s inconvenient to copy the C code into the project directory, then you can call the C function through dynamic linking, just add cgo LDFLAGS, specific implementation:
Put the previous sum.h and sum.c into ./dylib, then compile into a dynamic link library:
gcc -shared -o libsum.so sum.c
Then in the Go code:
package main
/*
#cgo LDFLAGS: -L./dylib/ -lsum
#include "sum.h"
*/
import "C"
import "fmt"
func main() {
fmt.Println(Sum(1, 2))
}
func Sum(a, b int) int {
return int(C.sum(C.int(a), C.int(b)))
}
Then execute CGO_ENABLED=1 go build -o cgo_example . to compile, and finally put cgo_example and libsum.so in the same directory to run.
The above example is not elegant enough. Suppose we want to call functions from some open-source libraries, which might be /usr/local/lib/libsum.dylib in our local development environment, and /usr/lib/libsum.so on the server, then hardcoding in the code will cause problems.
Actually, you don’t need to write #cgo ... in the code; you can specify where to find the dynamic link library (-L./dylib/) and the name of the link library (-lsum) through the CGO_LDFLAGS environment variable, so you can compile according to the actual installation situation on the machine.