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))
}