This is a small example on using C libraries in Go. Go is a relative newcomer in the world of programming languages and although it is very popular and there are many modules available for it there is the occasional need to interface with a pre existing C library. I came across the need for this when attempting to do some video manipulation on my GoPro videos. To be honest this probably would have been easier to write in Python but I am learning Go so I attempted it in Go. I rapidly found myself down a rabbit hole where the bindings I chose to use for ffmpeg in Go are not 100% implemented or I just dont know what I am doing so time to do some learning.

This is how I ended up having to write my first bit of C code since university. This implementation is meant to resemble a very simple version of what I am going to have to actually write to get some video processing done but this will be more of a ‘Hello World’ test run.

The first file to create is the header file person.h. This will define our types and method signatures.

/*
 * person.h
 * Copyright (C) 2019 Tim Hughes
 *
 * Distributed under terms of the MIT license.
 */

#ifndef PERSON_H
#define PERSON_H

typedef struct APerson  {
    const char * name;
    const char * long_name;
} APerson ;

APerson *get_person(const char * name, const char * long_name);

#endif /* !PERSON_H */

Next we need to create a implementation of our method in a .c file. This file can be called anything but we will call it person.c to make it memorable.

/*
 * person.c
 * Copyright (C) 2019 Tim Hughes
 *
 * Distributed under terms of the MIT license.
 */

#include <stdlib.h>
#include "person.h"


APerson *get_person(const char *name, const char *long_name){

    APerson *fmt = malloc(sizeof(APerson));
    fmt->name = name;
    fmt->long_name = long_name;

    return fmt;
};

This is now code complete as far as our libperson library is concerned so we can compile it then we will write a main function to test it out.

To generate a .so shared object library file you need to compile it with the -fPIC (position independent code) flag.

gcc -o person.o -c -fPIC person.c

This creates a object file .o which we can then use to create a .so file.

gcc -o libperson.so -shared person.o

You can see the difference using the file command.

$ file person.o libperson.so
person.o:     ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
libperson.so: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=8dfc4bd062c85c79406059ad7e1df5dd46f9d191, not stripped

I have done it the long way so that people can see what is going on but you can do it all in one step and best practice is to also add in the -Wall flag to get warnings and also the -g flag to get debugging output when compiling.

gcc -o libperson.so -Wall -g -shared -fPIC person.c

Now that we have a .so file we can create a main function to call the method in the .so

/*
 * person.c
 * Copyright (C) 2019 Tim Hughes
 *
 * Distributed under terms of the MIT license.
 */

#include <stdio.h>
#include "person.h"

int main(int argc, char** argv)
{
    APerson * of;
    of = get_person("tim", "tim hughes");
    printf("Hello C world: My name is %s, %s.\n", of->name, of->long_name);
    return 0;
}

To compile our hello.c file against the libperson.so we need to specify the directory to look in with -L and then the name of the library with the -l flag.

gcc -o hello -L. -lperson hello.c

Normally you would move the file into /usr/lib or /usr/lib64 or somewhere similar but for testing we can override the LD_LIBRARY_PATH environment variable to person to the directory we are in.

$ LD_LIBRARY_PATH=. ./hello
Hello C world: My name is tim, tim hughes

Now that we have a functioning shared library we can create our go code. To access a C library in go we can use the “C” pseudo package. This is explained in more detail at https://golang.org/cmd/cgo/#hdr-Using_cgo_with_the_go_command Just before the import of C we can put a comment, otherwise knows as the preamble. This comment may any snippets of C code that we want and it will be used as a header when the go compiler compiles the C parts of the code. In this case we just the person.h file. In the preamble we can also pass some instructions to the cgo compiler. The important on here is the LDFLAGS that tell the linker where to look for our libperson.so file in the same way we did when we compiled hello.c.

Here is our Go code.

//
// main.go
// Copyright (C) 2019 Tim Hughes
//
// Distributed under terms of the MIT license.
//
package main

/*
#cgo CFLAGS: -g -Wall
#cgo LDFLAGS: -L. -lperson
#include "person.h"
*/
import "C"
import (
	"fmt"
)

type (
	Person C.struct_APerson
)

func GetPerson(name string, long_name string) *Person {
	return (*Person)(C.get_person(C.CString(name), C.CString(long_name)))
}

func main(){
	var f *Person
	f = GetPerson("tim", "tim hughes")
	fmt.Printf("Hello Go world: My name is %s, %s.\n", C.GoString(f.name), C.GoString(f.long_name))
}

You will see that we have created a type Person that is of type C.struct_APerson Then we create a function GetPerson that returns a calls the get_person function in libperson. The return value of the function call is then type cast to *Person so that we have a normal Go pointer that is backed by a C struct.

If at this point you try and print the members of the struct using something like fmt.Println(f.name) you will get a hex number that looks like 0x1aa67e0 which is the integer representing a pointer. You will need to convert them to a Go string using the suitable named C.GoString() function.

To more convenient to access the members of the struct we can add accessors methods to the Person type. These are especially important if you are making a go library that someone else is going to use so that they don’t need to know the implementation underneath and don’t need to worry about C types when they are coding their application.

Here is the updated main.go file with the accessors methods added and the main function updated to use them.

//
// main.go
// Copyright (C) 2019 Tim Hughes
//
// Distributed under terms of the MIT license.
//
package main

/*
#cgo CFLAGS: -g -Wall
#cgo LDFLAGS: -L. -lperson
#include "person.h"
*/
import "C"
import (
	"fmt"
)

type (
	Person C.struct_APerson
)

func (p *Person) Name() string {
	return C.GoString(p.name)
}

func (p *Person) LongName() string {
	return C.GoString(p.long_name)
}


func GetPerson(name string, long_name string) *Person {
	return (*Person)(C.get_person(C.CString(name), C.CString(long_name)))
}


func main(){
	var f *Person
	f = GetPerson("tim", "tim hughes")
	fmt.Printf("Hello Go world: My name is %s, %s.\n", C.GoString(f.name), C.GoString(f.long_name))

	fmt.Printf("Hello Go world: My name is %s, %s.\n", f.Name(), f.LongName())
}

Final code is available at https://github.com/timhughes/cgoexample

I hope this has been useful reading.