Mischiefblog
I make apps for other people

Example cgo (Golang) app that calls a native library with a C structure

Posted by Chris Jones
On June 26th, 2014 at 12:47

Permalink | Trackback | Links In |

Comments Off on Example cgo (Golang) app that calls a native library with a C structure
Posted in General

Here’s a sample Go application that calls a native library. This uses cgo to link to the native library, pass a pointer to a C structure to a native function, and does it all from Go.

This example includes:

  • a Go main
  • a Go package
  • a static C library (shared libraries on OS X require tweaks to dyld)
  • instructions for compilation


The library is called hiya and is a hard way to implement “Hello, world!”

Create the C library
The C library will live in the same directory as the Go wrapper. I chose a structure that used mixed types to verify non-trivial structures.

$ export GOPATH=$HOME/go
$ mkdir -p $HOME/go/src/hiya
$ cd $HOME/go/src/hiya

The header file (hiya.h) defines the three functions I’ll call and the structure for the message to print.


typedef struct message_s {
  char message[255];
  int displayed;
} Message;

Message * create_message(char * msg);
void display_message(Message * message);
void free_message(Message * message);

The library implementation (hiya.c) performs the work.


#include "hiya.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
  
Message * create_message(char * msg) {
  Message * message;
  
  message = (Message *) malloc(sizeof(Message));
  if (message != NULL) {
    strcpy(message->message, msg);
    message->displayed = 0;
  }
  
  return message;
}
  
void display_message(Message * message) {
  printf("%s\n", message->message);
  message->displayed = 1;
}
  
void free_message(Message * message) {
  free(message);
}

Compile hiya into a library.


$ gcc -Wall -g -c hiya.c -o hiya.o
$ ar ruv libhiya.a hiya.o
$ ranlib libhiya.a

Alternatively, you can make hiya into a shared library object.


$ gcc -c -fPIC hiya.c -o hiya.o
$ gcc -shared -W1,-soname,libhiya.so.1 -o libhiya.so.1.0.0 hiya.o

If you make it a shared library, don’t forget to update your dynamic library path (dyld for OS X, LD_LIBRARY_PATH in Linux).

Write a quick tester C application (test.c) to verify your library works like you expect.


#include "hiya.h"
  
int main() {
  Message * msg = create_message("Hello, world");
  display_message(msg);
  free_message(msg);
  return 0;
}

Verify your tester.


$ gcc -o test -I../hiya/ -L../hiya/ -lhiya -Wall -g test.c
$ ./test

Create the Golang native library wrapper
Create the Go wrapper (hiya.go). I’m building mine in /Users/chris on OS X in the same directory where libhiya has been created. The explicit paths in the cgo flags are because of an environment issue where libhiya wasn’t found by the Go linker.


package hiya
  
/*
#cgo CFLAGS: -I/Users/chris/go/src/hiya
#cgo LDFLAGS: -L/Users/chris/go/src/hiya -lhiya
#include "hiya.h"
*/
import "C"
  
type Message C.Message
  
func CreateMessage(msg string) *C.Message {
  cMsg := C.CString(msg)
  return C.create_message(cMsg)
}
  
func DisplayMessage(msg *C.Message) {
  C.display_message(msg)
}
  
func FreeMessage(msg *C.Message) {
  C.free_message(msg)
}

You need to install the package for Go wrapper for the library to make it available to other packages.


$ go install hiya

Test the Go wrapper
Create a Go tester to make sure the library works. I created this in $GOROOT/src.


package main
  
import "hiya"
  
func main() {
  msg := hiya.CreateMessage("Hello, world!")
  hiya.DisplayMessage(msg)
  hiya.FreeMessage(msg)
}

Run this to verify the application works. You can also build it into a statically linked executable.


$ go run hiyatest.go
$ go build hiyatest.go
$ ls -al
-rwxr-xr-x 1 chris staff 444736 Jun 26 14:47 hiyatest

Comments are closed.