tech-lessons.in
Background by Giorgio Trovato on Unsplash
December 21, 2021

Invoking C Code from Golang

Posted on December 21, 2021  •  8 minutes  • 1522 words
Table of contents

The article attempts to explore Golang’s “C” package which allows invoking C code from Golang. Before we get into the idea of invoking C code from Golang, let’s see a use-case where this might be needed.

Interfacing with an existing C library

Let’s consider that we wish to develop a storage engine for pmem (persistent memory) in Golang. In order to develop this, we might want to use pmdk - persistent memory development kit which is written in C. This effectively means we want a way to bridge Golang and C code; invoke C code from Golang.

“C” package in Golang

Go provides a package called “C” to interface with C code. Some features provided by this package include -

C Code

Let’s start by creating a linked list in C which will be later invoked from Golang code. Let’s start with linkedlist.h file which defines a struct called Node, and a set of operations supported by linked list.

struct Node {
    int key;
    int value;
    struct Node *next;
};

void put(int key, int value);
int get(int key);

Our Node struct has a key, and a value which are of type integer and a pointer to the next node. It also supports 2 behaviors put and get.

Let’s add linkedlist.c and add put to begin with.

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

struct Node *head = NULL;
struct Node *current = NULL;

void put(int key, int value) {
    if (head == NULL) {
        head = (struct Node*)malloc(sizeof(struct Node));
        head -> key = key;
        head -> value = value;
        current = head;
    }
    else {
        struct Node *next = (struct Node*)malloc(sizeof(struct Node));
        next -> key = key;
        next -> value = value;
        current -> next = next;
        current = next;
    }
}

put does the following -

Let’s add get which will return a value by key.

int get(int key) {
    struct Node* node = head;
    while (node->key != key && node != NULL) {
        node = node -> next;
    }
    if (node == NULL) {
        return -1;
    }
    return node -> value;
}

get does the following -

Time to invoke C code from Golang

Let’s create a file linkedlist.go which will provide Put and GetBy behaviors. These functions will internally invoke C functions.

package linkedlist

// #cgo CFLAGS: -g -Wall
// #include "linkedlist.h"
import "C"

This is how the beginning of our go file looks like -

Let’s add Put in Golang.

func Put(key, value int) {
    C.put(C.int(key), C.int(value))
}

Golang Put does the following -

Let’s add GetBy.

func GetBy(key int) int {
    return int(C.get(C.int(key)))
}

Golang GetBy does the following -

Putting it all together -

package linkedlist

// #cgo CFLAGS: -g -Wall
// #include "linkedlist.h"
import "C"

func Put(key, value int) {
    C.put(C.int(key), C.int(value))
}

func GetBy(key int) int {
    return int(C.get(C.int(key)))
}

Let’s add a Golang test to see if this integration works or not.

package linkedlist_test

import (
"reflect"
"testing"
)
import "linkedlist"

func TestPutsASingleKeyValue(t *testing.T) {
    linkedlist.Put(10, 100)

    value := linkedlist.GetBy(10)
    if value != 100 {
        t.Fatalf("Expected %v, received %v", 100, value)
    }
}

Let’s Run the test using go test, and it should work.

Freeing the linked list

We would like to add another test which adds multiple key value pairs and gets a value by key. Before we do that, we would like to start with a fresh linked list for each test. In order to do that let’s add a behavior close in C linked list which frees all the nodes in the linked list.

void close() {
    struct Node* node;
    while (head != NULL) {
        node = head;
        head = head->next;
        free(node);
    }
}

close does the following -

Let’s invoke C's close from Golang

func Close() {
    C.close()
}

Let’s edit the existing test case and add a new one.

func TestPutsASingleKeyValue(t *testing.T) {
    defer linkedlist.Close()
    linkedlist.Put(10, 100)

    value := linkedlist.GetBy(10)
    if value != 100 {
        t.Fatalf("Expected %v, received %v", 100, value)
    }
}

func TestPutsMultipleKeyValues(t *testing.T) {
    defer linkedlist.Close()
    linkedlist.Put(10, 100)
    linkedlist.Put(20, 200)
    linkedlist.Put(30, 300)

    value := linkedlist.GetBy(20)
    if value != 200 {
        t.Fatalf("Expected %v, received %v", 200, value)
    }
}

As a part of these tests, we are now closing the linked list by invoking defer linkedlist.Close() which will internally free all the nodes.

Let’s get all the values

We would like to return all the values from linked list. As a first step, we would like to return a pointer to an integer from C code to Golang. Let’s code getAllValues in C.

int* getAllValues() {
    int len = length();
    int* values = malloc(len * sizeof(int));
    struct Node* node = head;
    int index = 0;

     while (node != NULL) {
         values[index] = node -> value;
         node = node -> next;
         index = index + 1;
     }
     return values;
}

getAllValues does the following -

Let’s also add length function -

int length() {
    int count = 0;
    struct Node* node = head;
    while (node != NULL) {
        node = node -> next;
        count = count + 1;
    }
    return count;
}

length does the following -

Time to invoke getAllValues from Golang.

func GetsAllValues() []int {
    length := int(C.length())
    intPointer := C.getAllValues()
    defer C.free(unsafe.Pointer(intPointer))

	slice := (*[1 << 28]C.int)(unsafe.Pointer(intPointer))[:length:length]

	var values []int
	for index := 0; index < length; index++ {
		values = append(values, int(slice[index]))
	}
	return values
}

GetAllValues does the following -

The expression slice := (*[1 << 28]C.int)(unsafe.Pointer(intPointer))[:length:length] is made up of 3 parts -

1. unsafePointer := unsafe.Pointer(intPointer) creates an unsafe pointer

2. arrayPtr := (*[1 << 28]C.int)(unsafePointer).`*[1 << 28]C.YourType` doesn't do anything itself, it is a type.
   Specifically, it is a pointer to an array of size 1 << 28, of C.YourType values.
   Statement 2) converts unsafePointer to a pointer of the type *[1 << 28]C.int

3. slice := arrayPtr[:length:length], slices the array into a Go slice

what-does-1-30c-yourtype-do-exactly-in-cgo

Let’s quickly add a golang test for GetAllValues.

func TestGetAllValues(t *testing.T) {
    defer linkedlist.Close()
    linkedlist.Put(10, 100)
    linkedlist.Put(20, 200)
    linkedlist.Put(30, 300)

    values := linkedlist.GetAllValues()
    expected := []int{100, 200, 300}
    
    if !reflect.DeepEqual(expected, values) {
        t.Fatalf("Expected %v, received %v", expected, values)
    }
}

That is it, run the test and get all the values from linked list.

Code

The code for this article is available here

References