Integrating C into GOLANG

Code for this post can be found here https://github.com/REAANDREW/coding-sessions

In this post I want to show how you can integate C code directly with GOLANG. To start with I will create a simple program in GOLANG which will assert on the result of invoking a factorial function with several different values and then simply print out a finish statement.

package main

import (  
   "fmt"
)

func assert(value bool, message string) {  
   if !value {
      panic(message)
   }
}

func factorial(input int) (result int) {  
   result = 1
   for counter := input; counter > 0; counter-- {
      result *= counter
   }
   return
}

func main() {  
   assert(factorial(6) == 720, "6! != 720")
   assert(factorial(5) == 120, "5! != 120")
   assert(factorial(4) == 24, "4! != 24")
   fmt.Println("All good!")
}

Write the above code into a file called main.go and compile it with go build -o factorial main.go. You will then end up with an executable called factorial. You can invoke this now and it should output All good!.

NOTE: the reason why I am compiling the above program by explicitly stating the main.go source file is because in the next step there will also exist a C file and the go compiler will automatically try to process if you do not limit what it should compile. To demonstrate, you can simply create an empty file called fubar.c and then run go build. You should end up seeing an error stating can't load package: package .: C source files not allowed when not using cgo: fubar.c

Next I will create a C function which will do the same as the above GOLANG factorial function but I will then change the GOLANG code to proxy the calculation to the C function instead. I will be able to delete the GOLANG factorial function after that.

Here is my version of the factorial function in C:

int factorial(int i){  
     int total = 1;
     int counter;
     for(counter = i; counter > 0; counter--){
       total *= counter;
     }
     return total;
}

Write the above code into a file called factorial.c at the same level as main.go. So you should have two files:

  • factorial.c
  • main.go

Before integrating into the GOLANG source I want to first makesure that the above C function does what it is intended to do. To keep things as simple as possible in this post I am just going to create another file (which will be deleted after this) which contains a couple of assertions.

#include <stdio.h>
#include <assert.h>

int factorial(int value);

int main(){  
     assert(factorial(6) == 720);
     assert(factorial(5) == 120);
     assert(factorial(4) == 24);
     printf("All good!\n");
}

Write out the above into a file called test-factorial.c and compile it with:

gcc -o test-factorial test-factorial.c --std=gnu11  

When you run the generated executable you should see All good! and if you do, then all is truly good.

Delete the test-factorial.c file.

Next I need need to go back into the main.go file and prepare it so that it proxies the call to calculate the factorial to the C function. Change the main.go file to have the following contents.

package main

import(  
   "fmt"
)

/*
#cgo CFLAGS: --std=gnu11

int factorial(int value);  
*/
import "C"

func assert(value bool, message string){  
   if !value {
      panic(message)
   }
}

func factorial(input int) (result int){  
   cInput := C.int(input)
   result = int(C.factorial(cInput))
   return
}

func main(){  
   assert(factorial(6) == 720, "6! != 720")
   assert(factorial(5) == 120, "5! != 120")
   assert(factorial(4) == 24, "4! != 24")
   fmt.Println("All good!")
}

You can see from the code that I need to use some special functions to interoperate the types from C to GOLANG and back. Also, using the C. prefix is the magic which lets me call the factorial method which I have defined and impemented in C. The fact that I have a factorial method inside of the golang now serves a purpose of acting as a wrapper to encapsulate the logic for interoperating with the C code. Compiling the above with go build -o factorial and then running should produce the same result i.e. All good!.

One thing which would be nicer with the above program is separating out the assertions from the main program and using an actual test route instead of using a console app AS the test harness. (hands up who hasn't done that before, no, no hands? ok) I actually left this out when I started writing this post as I actually didn't understand how to include source files which contains cgo in tests, but I did do some further investiagtion and I have found the way. Also, to avoid adding packages at this stage I am simply going to create two assertion methods to use in the program. Add the following code to a file called main_test.go .

package main

import (  
   "testing"
)

func assertEqual(t *testing.T, actual interface{}, expected interface{}){  
   if actual != expected {
      t.Errorf("%v does not equal %v",actual, expected)
   }
}
func assertNotEqual(t *testing.T, actual interface{}, expected interface{}){  
   if actual == expected {
      t.Errorf("%v equals %v",actual, expected)
   }
}

func TestFactorialSucceeds(t *testing.T){  
   assertEqual(t, factorial(5), 120)
}

func TestFactorialFails(t *testing.T){  
   assertNotEqual(t, factorial(5), 1)
}

You should now have the following files in your directory:

  • factorial.c
  • main.go
  • main_test.go

Now run the actual tests using the command go test and you should see output similar to the following:

PASS  
ok      _/home/vagrant/blog-c-with-go   0.004s  

To finish this off nice and clean I will amend the main program so that it takes the value from STDIN with which to calculate the factorial and then write the result to STDOUT.

package main

import (  
    "fmt"
    "os"
    "strconv"
)

/*
#cgo CFLAGS: --std=gnu11

int factorial(int value);  
*/
import "C"

func assert(value bool, message string) {  
    if !value {
        panic(message)
    }
}

func factorial(input int) (result int) {  
    cInput := C.int(input)
    result = int(C.factorial(cInput))
    return
}

func main() {  
    arg := os.Args[1]
    input, err := strconv.Atoi(arg)
    if err != nil{
        panic("You must supply an integer as the first argument")
    }
    result := fmt.Sprintf("%d! = %d", input, factorial(input))
    fmt.Println(result)
}

Now compile the program with go build -o factorial and run with a value like ./factorial 5 which will produce the output:

5! = 120