Tuesday, 24 July 2012

Experimenting with Cgo - Part 4: C Macros

One of the most frustrating parts of working on Goncurses is the fact that ncurses increasingly uses more and more macros. C macros cause a lot of problems for cgo, which isn't all that surprising, because C macros can cause a lot of problems period.

In order to circumvent C macros you have to provide proper C function wrappers around them. Depending on the library you are trying to port, this could be quick and simple or it could be long and tedious. I will demonstrate a very simple example.

1. You will need to create three files: a go wrapper, a C header and a C source file. The two C files must share the same name.

2. Identify the macro you need to create a wrapper for. For the purpose of this example we will be using a macro called MY_MACRO. In your header file, write a function prototype for the wrapper.

int my_macro_wrapper(const int x);

3. In the C source file, write a definition for the function prototyped in your header which calls the macro you are wrapping.

int my_macro_wrapper(const int x)
{
return MY_MACRO(x);
}

4. In your .go file you will need to include your wrapper header and then wrap the C file in a go function.

// #include "macro_wrapper.h"
import "C"
...
C.my_macro_wrapper(2)

That's all there is to it!

In the complete, trivial example below you'll notice I used the macro to call another function. You might think it a good idea to circumvent the macro altogether and simply call my_function() directly in Go. While this would probably work, and certainly would in this example, it defeats the purpose for why the author of the C library used a macro in the first place. There are several reasons why this could be a bad idea and I recommend using the C API as the author intended.

Complete sources follow:

***
macro_wrapper.h:
***

int my_function(const int x);


#define MY_MACRO(x) my_function(x)


int my_macro_wrapper(const int x);


***
macro_wrapper.c
***

#include "macro_wrapper.h"


int my_function(const int x) 
{
return x;
}


int my_macro_wrapper(const int x) 
{
return MY_MACRO(x);
}


***
library_wrapper.c
***

package main


// #include "macro_wrapper.h"
import "C"


import "fmt"


func main() {
// Uncomment the following line to produce error
// fmt.Println(C.MY_MACRO(2))
fmt.Println(C.my_macro_wrapper(2))
}