50

Ever since Go 1.5 came out, I started taking another look at how I could integrate it into an existing project of mine.

The project's codebase is written entirely in C for low level access to to hardware and other fun stuff. However, some of the higher level things are tedious, and I would like to start writing them in a higher level language (Go)

Is there any way I can call Go code from a C program? I installed Go 1.5, which added -buildmode=c-archive (https://golang.org/s/execmodes) which I am trying to get working.

However, I can't seem to get Go to generate the appropriate header files to allow my project to actually compile. When I generate the archive, I see the function in the exported symbols (using objdump), but without the header files to include gcc complains about the function not existing (as expected)

I'm quite new to Go - however, I love the language and would like to make use of it. Is there any idiomatic way ("idiomatic" gets used a lot in the world of Go I see...) to get this to play nicely with each other?

The reason I asked this question and specifically mentioned Go 1.5 is that according to this document, https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1#heading=h.1gw5ytjfcoke Go 1.5 added support for non-Go programs to call Go code. Specifically, mentioned under the section "Go code linked into, and called from, a non-Go program"

dreadiscool
  • 1,598
  • 3
  • 18
  • 31
  • 1
    Are you exporting via cgo? The document you link to says: "The only callable symbols will be those marked as exported (by any package), as described in the [cgo documentation](https://golang.org/cmd/cgo/#hdr-C_references_to_Go)." That link to the cgo documentation mentions a generated `_cgo_export.h`. – Dave C Aug 25 '15 at 23:53
  • 1
    An earlier question said the answer is no. I'd be curious if anything has changed in 1.5, but I doubt it: http://stackoverflow.com/questions/6125683/call-go-functions-from-c – Gabriel Southern Aug 25 '15 at 23:55
  • 2
    The reason I mentioned Go 1.5 was because that document specifically mentions being able to create shared object files or archive files that can be called by systems that don't expect Go code It's marked under the section called "Go code linked into, and called from, a non-Go program" in this link https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit?pli=1#heading=h.1gw5ytjfcoke – dreadiscool Aug 26 '15 at 01:07
  • Dave C's on the trail--https://blog.filippo.io/building-python-modules-with-go-1-5/ has more (in the context of connecting Go to Python via `c-shared` archive and `cgo` exports), but I don't have the time to piece that into a coherent answer right now. It does seem that you can use Go from C now, though. – twotwotwo Aug 26 '15 at 01:47
  • I mentioned cgo **not** as a way to interact with C code, but as the way you need to **mark** which functions you wish to be exported and callable from C (basically just a `//export …` comment). As my comment mentions, this is described in the exact document linked to. – Dave C Aug 26 '15 at 17:21

2 Answers2

70

To build an archive callable from C, you will need to mark them as exported CGo symbols.
For example, if I create a file foo.go with the following contents:

package main

import (
    "C"
    "fmt"
)

//export PrintInt
func PrintInt(x int) {
    fmt.Println(x)
}

func main() {}

The important things to note are:

  • The package needs to be called main
  • You need to have a main function, although it can be empty.
  • You need to import the package C
  • You need special //export comments to mark the functions you want callable from C.

I can compile it as a C callable static library with the following command:

go build -buildmode=c-archive foo.go

The results will be an archive foo.a and a header foo.h. In the header, we get the following (eliding irrelevant parts):

...
typedef long long GoInt64;
...
typedef GoInt64 GoInt;
...
extern void PrintInt(GoInt p0);
...

So that's enough to call the exported function. We can write a simple C program that calls it like so:

#include "foo.h"

int main(int argc, char **argv) {
    PrintInt(42);
    return 0;
}

We can compile it with a command like:

gcc -pthread foo.c foo.a -o foo

The -pthread option is needed because the Go runtime makes use of threads. When I run the resulting executable it prints 42.

James Henstridge
  • 42,244
  • 6
  • 132
  • 114
  • misc question: is there any way to get this to work when cross-compiling for windows from OSX? this part will fail: ```GOOS=windows GOARCH=amd64 go build -buildmode=c-archive foo.go``` – mwag Oct 16 '16 at 04:05
  • 1
    IIRC, CGo gets disabled by default when cross-building. You can re-enable it with `CGO_ENABLED=1`, but you'll also need to provide C/C++ compilers for the target arch with `CC_FOR_TARGET` and `CXX_FOR_TARGET` as described in https://golang.org/cmd/cgo/. I don't have specific advice for building for Windows on OSX though. – James Henstridge Oct 17 '16 at 09:13
  • How would you do it on Windows? Especially what would be the replacement for `pthread`? – Royi Apr 18 '21 at 12:18
  • @Royi: It's ages since I've developed for Windows. If you wouldn't normally need a special compiler flag to build a C program that uses threads, then odds are you don't need to do anything special here either. – James Henstridge Apr 19 '21 at 15:44
0

The code above work just fine, but gcc will complain about functions and headers.

The includes should be:

#define _GNU_SOURCE
#include <stdio.h>
#include "mygopkg.h"

If you forget the #define _GNU_SOURCE, the gcc will complain:

warning: implicit declaration of function 'asprintf'; did you mean 'vsprintf'? [-Wimplicit-function-declaration]

If you forget the #include "mygopkg.h", the gcc will complain:

warning: implicit declaration of function 'PrintString' [-Wimplicit-function-declaration]

The last but not less important. The build command line I recommend for production code is:

go build -ldflags "-s -w"  -buildmode c-archive -o mygopkg.a

It'll save you 53% size of final mygopkg.a.