Monday, 26 September 2011

Experimenting with Cgo - Part 1

** Modified: Updated to comply with Go 1.1 release. **

This is the first in a planned series of posts on cgo. Cgo is a program in the Go language tool suite that allows programmers to wrap C language libraries for use in Go. I have found it to be an incredibly useful tool and I am sure will help provide Go with the ability to grow it's third-party, non-standard library more quickly.

I am currently designing some software for personal business use which would utilize the ncurses library as an interface. There is already a curses wrapper library available for Go but I wasn't entirely satisfied by the completeness of the library. For one, the mouse isn't implemented at all, along with a myriad of other functions. Nor are the form, menu or panel libraries. So I decided that this would the perfect time to learn cgo.

To create a wrapper library for a C library you need to take the following steps:

1. Setup your build environment. I recommend using a sub-directory in your home directory. On Linux you would use:


$ cd ~
$ mkdir -p godev/src/mylibwrapper


Of course, you can change the 'godev' to whatever you want and 'mylibwrapper' to the name of the library you're creating a wrapper for but 'src' must be a sub-directory of your development hierarchy.

You will also need to set your $GOPATH to this new directory. On Linux you might use:


$ export GOPATH="$GOPATH:/home/username/godev"


Take care not to add a slash at the end of the directory name as the go tools won't like it.

2.  Within your wrapper library you need to include the C library header(s) you need to use followed by the C import directive:



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



It is essential that 'import "C"' is on its own, directly after the commented C code in order for cgo to work properly. Or, at all for that matter. You can write your own C code prior to the import call and use either of Go's comment styles. 


3. If you need to pass any compile flags or link to the C library, like the C math library, you would use cgo's LDFLAGS and CFLAGS:



// #cgo LDFLAGS: -lm



4. To access any of the C functions from your Go code, you use the C namespace. Same goes for C types. As a matter of fact, no Go types are available in C and vice versa. Therefore, everything you need to pass to or from a C function must be cast to or from a C or Go type. Unfortunately, strings aren't so easy. In order to convert a Go string to a C string, you need to use the CString function. For a good example and explanation, see Andrew Gerrards blog post on the subject.



func Pow(b, e float64) float64 {
    return float64(C.pow(C.double(b), C.double(e)))
}



5. Run 'go build' to compile your code. This step is not strictly necessary as installing automatically builds the library for you and will not install it if the build stage fails.

6. In order to test your new wrapper, you'll need to run 'go install' and then import your library into some Go source.

That's it, start to finish. To view a complete example:


godev/src/mylibwrapper/mylibwrapper.go:

package mylibwrapper

// #cgo LDFLAGS: -lm
// #include <math.h>
import "C"

func Pow(b, e float64) float64 {
    return float64(C.pow(C.double(b), C.double(e)))
}


godev/src/cgoexample/example.go:

package main

import (
    "mylibwrapper"
    "fmt"
)

func main() {
    b, e := 5.0, 2.0
    f := mylibwrapper.Pow(b, e)
    fmt.Println(b, "raised to the power of", e, "is", f)
}