How to include C code in your Go package

It looks like Go 1.4 will remove support for Go packages containing C code (as described below, don’t confuse this with CGO), so enjoy it while it lasts.


This is a short post designed to illustrate how Go package authors can write package level functions in C and call them from Go code without using cgo. The code for this article is available on GitHub, https://github.com/davecheney/ccode.

Some warnings

Before we start, there are a few warnings that need to be spelled out

  • Using C code is inherently unsafe, not just because it unholsters all the C footguns, but because you can address any symbol in the runtime. With great power comes great responsibility.
  • The Go 1 compatibility guarantee does not extend to C code.
  • C functions cannot be inlined.
  • Escape analysis cannot follow values passed into C functions.
  • Code coverage does not extend to C functions.
  • The C compilers (5c, 6c, 8c) are not as optimised as their companion Go compilers, you may find that the code generated is not as efficient as the same code in Go.
  • You are writing plan 9 style C code, which is a rough analogue of C89.

Returning a value

The first example is a simple function called True that always returns true.

void ·True(bool res) {
        res = true;
        FLUSH(&res);
}

Even with this simple example there is a lot going on, let’s start with the function signature. The signature of True is void ·True(bool res). The void return code is required as all C to Go interworking is done via arguments passed on the stack. The Interpunkt, the middle dot, ·, is part of the package naming system in Go. By proceeding the name of the function with · we are declaring this function is part of the current package. It is possible to define C functions in other packages, or to provide a package name before the interpunkt, but it gets complicated when your package is heavily namespaced and so is beyond the scope of this article.

The next part of the function signature is the return argument specified in the C declaration style. Both calling and return arguments are supplied as parameters to any C function you want to call from Go. We’ll see in a moment how to write the corresponding forward declaration.

Moving on to the body of the function, assigning true to res is fairly straight forward, but the final line, FLUSH(&res) needs some explanation. Because res is not used inside the body of the function a sufficiently aggressive compiler may optimise the assignment away. FLUSH is used to ensure the final value of res is written back to the stack.

The forward declaration

To make the True function available to our Go code, we need to write a forward declaration. Without the forward declaration the function is invisible to the Go compiler. This is unrelated to the normal rules for making Go a symbol public or private via a capital letter.

A forward declaration for the True function looks like this

// True always returns true.
func True() bool

The forward declaration says that True takes no arguments and returns one boolean argument. As this is a normal Go function, you can attach a comment describing the function which will appear in godoc (comments on the function in C code will not appear in documentation).

That is all you need to do make True available to Go code in your package. It should be noted that while True is a public function, this was not required.

Passing arguments to C functions

Extending from the previous example, let’s define a function called Max which returns the maximum of two ints.

void ·Max(intptr a, intptr b, intptr res) {
        res = a > b ? a : b;
        FLUSH(&res);
}

Max is similar to the previous function; the first two arguments are function arguments, the final is the return value. Using res for the name of the return argument is not required, but appears to be the convention used heavily throughout the standard library.

The type of a and b is intptr which is the C equivalent of Go’s platform dependant int type.

The forward declaration of Max is shown below. You can see the how function arguments and return values map between Go and C functions.

// Max returns the maximum of two integers.
func Max(a, b int) int

Passing addresses

In the previous two examples we have passed values to functions and returned copies of the result via the stack. In Go, all arguments are passed by value, and calling to C functions is no different. For this final example we’ll write a function that increments a value by passing a pointer to that value.

void ·Inc(intptr* addr) {
        *addr+=1;        
        USED(addr);
}

The Inc function takes the address of a intptr (a *int in Go terms), dereferences it, increments it by one, and stores the result at the address addr. The USED macro is similar in function to FLUSH and is used mainly to silence the compiler.

Looking at the forward declaration, we define the function to take a pointer to the int to be incremented.

// Inc increments the value of the integer add address p.
func Inc(p *int)

Putting it all together

To demonstate using these C defined functions in Go code I’ve written a few tests which exercise the code. The code for the tests are here, and the results of running the tests are shown below.

% go test -v github.com/davecheney/ccode
=== RUN TestTrue
--- PASS: TestTrue (0.00 seconds)
=== RUN TestMax
--- PASS: TestMax (0.00 seconds)
=== RUN TestInc
--- PASS: TestInc (0.00 seconds)
PASS
ok      github.com/davecheney/ccode     0.005s

Conclusion

In this short article I’ve shown how you can write a Go package that includes functions written in C. While a quite niche use case, it may come in handy for someone and also lays important groundwork for writing packages containing functions in raw assembler.

One thought on “How to include C code in your Go package

  1. Pingback: How to include C code in your Go package (without using cgo) | Rocketboom

Comments are closed.