Manoj Gupta
by Manoj Gupta

Categories

Tags

cgo allows Go and C programs to interoperate. This is very useful feature which lets C existing libraries to be called by Go. On the other hand Go libraries can also be called by C code.

This post is to show examples how to do certain things using C and Go

Call C code from Go Code

Go provides a pseudo package called “C” to interface with C libraries. This can be used easily for calling C function written within the same Go file or in a seperate C file.

Call C code within Go file

Create a new folder in your $GOPATH/src. I have created folder name cgo-bridge. Inside this folder create a new file main.go.

Add the following on the top

package main

/*
#cgo CFLAGS: -g -Wall
#include <stdio.h>

void cPrint(void) {
        printf("%s: Executing C code\n", __func__);
}

int cAdd(int x, int y) {
    return x+y;
}

*/
import "C"

Notice that there should not be any newline between comment section of cgo and import "C" statement. Here two functions are defined to show how parameters and return value are passed.

Next add the following section to call C functions.

import (
        "fmt"
)

func main() {
        callInternal()
}

// callInternal .. calls C functions within Go file
func callInternal() {
        // print from C code
        C.cPrint()

        // Passing parameter and receiving value
        x := C.int(5)
        y := C.int(10)
        z := C.cAdd(x, y)

        // Normal Go fmt print
        fmt.Println(z)
}

Build the program using go build. Note that you can not use go run main.go directly.

ubuntu@node1: $go build main.go
# command-line-arguments
cgo-gcc-prolog: In function ‘_cgo_23fb3eb884a7_Cfunc_cPrint’:
cgo-gcc-prolog:67:49: warning: unused variable ‘_cgo_a’ [-Wunused-variable]

Do not use -Wall in cgo CFLAGS if you do not want to see this warning. This is a general issue in Go. Read more in the github thread

Now run the built binary

ubuntu@node1: $./cgo-bridge
cPrint: Executing C code
15

Call C code external to Go file

Now, let’s see how to call C code which is external to Go code.

Create a hello.h file and add the following code.

#ifndef _HELLO_H
#define _HELLO_H

struct hello_s {
    const char *name;
    int year;
};

int hello(struct hello_s *h, char *s);

#endif

Create a hello.c file and add the following code.

#include <stdio.h>
#include <stdlib.h>
#include "hello.h"

int hello(struct hello_s *h, char *msg)
{
    int n;
    n = sprintf(msg, "%s (%d)!", h->name, h->year);
    return n;
}

Check that code compiles by ensuring hello.o is generated after compilation of C code.

ubuntu@node1:$ gcc -c hello.c

ubuntu@node1:$ ls -l
total 16
-rw-rw-r-- 1 ubuntu ubuntu  161 Jun 15 12:42 hello.c
-rw-rw-r-- 1 ubuntu ubuntu  156 Jun 15 12:42 hello.h
-rw-rw-r-- 1 ubuntu ubuntu 1584 Jun 15 12:43 hello.o
-rw-rw-r-- 1 ubuntu ubuntu  579 Jun 15 12:34 main.go

Change the main.go to include the following:

C Includes in cgo section

#include "hello.h"

Golang import library in go import library section

import "unsafe"

Add new function to call external C code

// callExternal .. calls C functions outside Go file
func callExternal() {
        name := C.CString("Hello World")
        defer C.free(unsafe.Pointer(name))

        year := C.int(2020)

        g := C.struct_hello_s{
                name: name,
                year: year,
        }

        ptr := C.malloc(C.sizeof_char * 1024)
        defer C.free(unsafe.Pointer(ptr))

        size := C.hello(&g, (*C.char)(ptr))

        b := C.GoBytes(ptr, size)
        fmt.Println(string(b))
}

and call this function from main()

        callExternal()

Build and execute the code

ubuntu@node1: $go build
ubuntu@node1: $ls
cgo-bridge  hello.c  hello.h  main.go
ubuntu@node1: $./cgo-bridge
cPrint: Executing C code
15
Hello World (2020)!

Code Listing

src/cgo-bridge/hello.h

#ifndef _HELLO_H_
#define _HELLO_H_

struct hello_s {
    const char *name;
    int year;
};

int hello(struct hello_s *h, char *msg);

#endif // _HELLO_H_

src/cgo-bridge/hello.c

#include <stdio.h>

#include "hello.h"

int hello(struct hello_s *h, char *msg)
{
    int n;
    n = sprintf(msg, "%s (%d)!", h->name, h->year);
    return n;
}

src/cgo-bridge/main.go

package main

/*
void cPrint(void) {
       printf("%s: Executing C code\n", __func__);
}

int cAdd(int x, int y) {
    return x+y;
}
*/
import "C"

import (
        "fmt"
)

func main() {
        callInternal()
}

//export myprint
func myprint(i C.int) {
        fmt.Printf("i = %v\n", uint32(i))
}

// callInternal .. calls C functions within Go file
func callInternal() {
        // print from C code
        C.cPrint()

        // Passing parameter and receiving value
        x := C.int(5)
        y := C.int(10)
        z := C.cAdd(x, y)

        // Normal Go fmt print
        fmt.Println(z)
}

Call Go code from C Code

Here is a example of Go code called from C code. Important things is to export the function using export keyword, do a forward declaraction of these function in CGO section and have some way to call these (I used wrappers).

Three examples shows:

  • goPrintInt - Prints an integer passed from C code
  • goMultiply - Receives two arguments to multiply and return the value
  • goHandleData - Receives a pointer data and len to be copied.

src/cgo-callback/main.go

package main

/*
#include <stdio.h>
#include <string.h>

extern void goPrintInt(int i);
extern int goMultiply(int i, int j);
extern void goHandleData(char *data, int length);

char buf[1024];

static inline void wrapperGoPrintInt(void) {
    printf("\n%s: Enter\n", __func__);
    goPrintInt(100);
}

static inline int wrapperGoMultiply(int a, int b) {
    printf("\n%s: Enter\n", __func__);
    return goMultiply(a, b);
}

static inline void wrapperGoHandleData(void) {
    printf("\n%s: Enter\n", __func__);
    buf[0] = 'c';
    char *s = "First Last Name";

    goHandleData(&buf[0], sizeof(buf));

    printf("\n");
    goHandleData(s, strlen(s));
}

*/
import "C"
import (
        "fmt"
        "unsafe"
)

func main() {
    // print from C code
    C.wrapperGoPrintInt()

    a := 3
    b := 5
    // multiple in C code
    c := C.wrapperGoMultiply(C.int(a), C.int(b))
    fmt.Println(a, "*", b, "=", int(c))

    C.wrapperGoHandleData()
}

//export goPrintInt
func goPrintInt(i C.int) {
    fmt.Printf("i = %v\n", uint32(i))
}

//export goMultiply
func goMultiply(a C.int, b C.int) C.int {
    return a * b
}

//export goHandleData
func goHandleData(data *C.char, length C.int) {
    md := C.GoBytes(unsafe.Pointer(data), C.int(length))
    fmt.Println("len: ", length)
    fmt.Println("md: ", string(md))
}