Essential Go Calling C from GO with cgo  Suggest an edit

Cgo: First steps tutorial

Some examples to understand the workflow of using Go C Bindings

What

In Go you can call C programs and functions using cgo. This way you can easily create C bindings to other applications or libraries that provides C API.

How

All you need to do is to add a import "C" at the beginning of your Go program just after including your C program:

//#include <stdio.h>
import "C"

With the previous example you can use the stdio package in Go.

If you need to use an app that is on your same folder, you use the same syntax than in C (with the " instead of <>)

//#include "hello.c"
import "C"
IMPORTANT: Do not leave a newline between the include and the import "C" statements or you will get this type of errors on build:
# command-line-arguments
could not determine kind of name for C.Hello
could not determine kind of name for C.sum

The example

On this folder you can find an example of C bindings. We have two very simple C “libraries” called hello.c:

//hello.c
#include <stdio.h>

void Hello(){
    printf("Hello world\n");
}

That simply prints “hello world” in the console and sum.c

//sum.c
#include <stdio.h>

int sum(int a, int b) {
    return a + b;
}

…that takes 2 arguments and returns its sum (do not print it).

We have a main.go program that will make use of this two files. First we import them as we mentioned before:

//main.go
package main

/*
  #include "hello.c"
  #include "sum.c"
*/
import "C"

Hello World!

Now we are ready to use the C programs in our Go app. Let’s first try the Hello program:

//main.go
package main

/*
  #include "hello.c"
  #include "sum.c"
*/
import "C"


func main() {
    //Call to void function without params
    err := Hello()
    if err != nil {
        log.Fatal(err)
    }
}

//Hello is a C binding to the Hello World "C" program. As a Go user you could
//use now the Hello function transparently without knowing that it is calling
//a C function
func Hello() error {
    _, err := C.Hello()    //We ignore first result as it is a void function
    if err != nil {
        return errors.New("error calling Hello function: " + err.Error())
    }

    return nil
}

Now run the main.go program using the go run main.go to get print of the C program: “Hello world!”. Well done!

Sum of ints

Let’s make it a bit more complex by adding a function that sums its two arguments.

//sum.c
#include <stdio.h>

int sum(int a, int b) {
  return a + b;
}

And we’ll call it from our previous Go app.

//main.go
package main

/*
#include "hello.c"
#include "sum.c"
*/
import "C"

import (
    "errors"
    "fmt"
    "log"
)

func main() {
    //Call to void function without params
    err := Hello()
    if err != nil {
        log.Fatal(err)
    }

    //Call to int function with two params
    res, err := makeSum(5, 4)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Sum of 5 + 4 is %d\n", res)
}

//Hello is a C binding to the Hello World "C" program. As a Go user you could
//use now the Hello function transparently without knowing that is calling a C
//function
func Hello() error {
    _, err := C.Hello() //We ignore first result as it is a void function
    if err != nil {
        return errors.New("error calling Hello function: " + err.Error())
    }

    return nil
}

//makeSum also is a C binding to make a sum. As before it returns a result and
//an error. Look that we had to pass the Int values to C.int values before using
//the function and cast the result back to a Go int value
func makeSum(a, b int) (int, error) {
    //Convert Go ints to C ints
    aC := C.int(a)
    bC := C.int(b)

    sum, err := C.sum(aC, bC)
    if err != nil {
        return 0, errors.New("error calling Sum function: " + err.Error())
    }

    //Convert C.int result to Go int
    res := int(sum)

    return res, nil
}

Take a look at the “makeSum” function. It receives two int parameters that need to be converted to C int before by using the C.int function. Also, the return of the call will give us a C int and an error in case something went wrong. We need to cast C response to a Go’s int using int().

Try running our go app by using go run main.go

$ go run main.go
Hello world!
Sum of 5 + 4 is 9

Generating a binary

If you try a go build you could get multiple definition errors.

$ go build
# github.com/sayden/c-bindings
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/hello.o: In function `Hello':
../../go/src/github.com/sayden/c-bindings/hello.c:5: multiple definition of `Hello'
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/main.cgo2.o:/home/mariocaster/go/src/github.com/sayden/c-bindings/hello.c:5: first defined here
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/sum.o: In function `sum':
../../go/src/github.com/sayden/c-bindings/sum.c:5: multiple definition of `sum`
/tmp/go-build329491076/github.com/sayden/c-bindings/_obj/main.cgo2.o:/home/mariocaster/go/src/github.com/sayden/c-bindings/sum.c:5: first defined here
collect2: error: ld returned 1 exit status

The trick is to refer to the main file directly when using go build:

$ go build main.go
$ ./main
Hello world!
Sum of 5 + 4 is 9
Remember that you can provide a name to the binary file by using -o flag go build -o my_c_binding main.go

I hope you enjoyed this tutorial.

  ↑ ↓ to navigate     ↵ to select     Esc to close