Golang Cgo

Topics Covered

Overview

Go (also known as Golang) is a programming language developed by Google. Golang CGO is a Go package that enables developers to invoke C code from Go code and vice versa. This means that Go allows developers to build high-level, high-performance code while maintaining existing C libraries and code. CGO will enable you to write Go code that interacts with C code and C code that interacts with Go code.

CGO includes a collection of Go keywords that help call C functions, define C variables and pointers, and pass Go data structures to C functions and C data structures to Go functions. CGO also allows you to provide callbacks for C functions.

Introduction

Golang CGO is a package in Go that enables developers to interact with C code in their Go programs. With CGO, developers can call C functions, use C variables and pointers, and pass data structures between Go and C. It provides a way to set callbacks so that C functions can call Go functions. However, CGO has certain limitations, such as not supporting C++, C++ exceptions, and C++ thread-local storage. Additionally, CGO can be more complex to use and may make the code less portable, as it relies on the presence of C libraries on the system where the code is executed.

Let's take an example in which we C code from GO.

Output

From the above code, we conclude,

  • The import of "C" is immediately followed by a comment, which serves as a header when compiling the C sections of the package.
  • We may define a C function like sum() and use the C package to invoke it.
  • The C package provides access to C types such as int and structs you define. We can convert our Go int to C.int and vice versa.

The Basics

When we use golang CGO, we must consider the following basic points:

  • There must be no blank spaces between the cgo comment and the import declaration.
  • Import "C" provides a special packet that enables the Go tool to compile with CGO. The comments above this import are compiled using GCC(64-bit) compiler.
  • Import "C" cannot be combined with additional imports in a parenthesized, "factored" import statement. You must write numerous import statements, such as:

Invoking C in Golang

It is possible to invoke both top-level Go functions and function variables from C code called from Go code using cgo. Let's see some of them:

Global Functions

Go provides its functions to C code through you must first create a C wrapper function for the Go function and then export the Go function by adding the //export comment followed by the wrapper function name.

Let's take an example of two files foo.go and foo.c

foo.go

foo.c

Output

Function Variables

Go code cannot transmit a function value straight to C due to the pointer passing rules. Instead, an indirection must be used. The program given below demonstrates how to call a Go callback from C code. This example employs a registry with a mutex, but there are other different approaches to mapping from a value provided to C to a Go function.

Go Strings and C Strings

In C programming, a string is a sequence of characters that ends with the null character '0'. Strings are defined as an array of characters. The distinction between a character array and a string is that the string ends with the unique character '0'.

Strings in Go differ from those in other languages such as Java, C++, Python, etc. It is a sequence of variable-width characters, with each character represented by one or more bytes encoded using UTF-8. In other terms, strings are an immutable chain of arbitrary bytes (including zero-valued bytes), or strings are a read-only slice of bytes whose bytes can be expressed in Unicode text using UTF-8 encoding.

One thing to keep in mind is that C.CString() will allocate and return a new string of the appropriate length. That means the C string will not be garbage collected and must be freed by you.

Turning C Arrays Into Go Slices

You can turn C arrays into Go slices by using reflect.SliceHeader struct and the unsafe package in GO. The reflect.SliceHeader struct allows you to create a Go slice from a C array by specifying the array's pointer, length, and capacity. The unsafe package allows you to create a pointer to a C array and pass it to the reflect.SliceHeader struct.

Let's take an example to turn a C array into Go slices

Output

The following code simply turn C arrays into Go slices and prints the value stored in it.

It is important to remember that this method is unsafe because it enables Go code to access memory that it shouldn't be able to access, and it is not advised to use it in real-world environments.

Also, when passing the C array to Go, you must ensure that the array is not freed or modified while the Go slice is still in use. Otherwise, it will cause undefined behavior.

Common Pitfalls

There are a few common pitfalls that developers may encounter when using CGO in Go.

Struct Alignment Issues

One problem that may arise when using CGO is that certain data types have stricter alignment requirements in Go than in C. This means that if a struct is aligned correctly in C but not in Go, it cannot be represented in Go. This can lead to errors or unexpected behavior when passing data between the two languages.

The complex64 data type in Go has a stricter alignment requirement of 8 bytes, while C's complex float is represented as a struct with a 4-byte alignment (since C handles the complex float internally as a struct {float real; float imaginary;}, not a basic type). This means that a struct containing a complex float in C may not have a corresponding representation in Go. To resolve this issue, one can either relocate the complex float to an 8-byte aligned position or use a specific form that forces the struct to align to 8 bytes, even though it will result in a wastage of 4 bytes.

If you need control over the struct layout and it contains a complex float with 4-byte alignment, which is incompatible with Go's 8-byte alignment requirement. In that case, you will have to create C functions to access the struct's fields, as cgo won't be able to convert it into a corresponding Go struct.

Memory Management

Memory management mechanisms in Go and C differ, which might cause problems when transporting data between the two languages. Developers must know how Go and C allocate and free memory and ensure that data is properly passed and freed to avoid memory leaks or other issues.

Type Conversions

Data types and conventions differ between Go and C, which might cause problems when passing data between the two languages. Developers must be aware of the types and conventions used in both languages and take care to execute suitable type conversions when passing data.

Complexity

Golang CGO code is less portable than pure Go code, as it relies on C libraries available on the system where the code is run. This can make deploying Go programs that use CGO on different systems or platforms more difficult.

Windows

To use golang cgo on Windows, you must first install a GCC compiler (for example, MinGW-w64) and have gcc.exe (and so on) in your PATH environment variable before building using cgo.

//export and Definition in Preamble

You cannot use definitions like (int n; or int f() {return 0; }) in the C code if the Go source file uses any //export directives. Note: You can still use declarations (extern int f();).

Environmental Variables

Go os.Getenv() doesn't see variables set by C.setenv()

Tests

cgo cannot be used by _test.go files.

Conclusion

  • Golang CGO is a Go package that enables developers to invoke C code from Go code and vice versa.
  • Golang CGO will enable you to write Go code that interacts with C code and C code that interacts with Go code.
  • Golang CGO can be more complex to use and may make the code less portable, as it relies on the presence of C libraries on the system where the code is executed.
  • When we use CGO in Go, we must consider the following basic points:
    • There must be no blank spaces between the cgo comment and the import declaration.
    • import "C" provides a special packet that enables the Go tool to compile with CGO. The comments above this import are compiled using GCC(64-bit) compiler.
    • import "C" cannot be combined with additional imports in a parenthesized, "factored" import statement.
  • Strings in Go differ from those in other languages such as Java, C++, Python, etc. It is a sequence of variable-width characters, with each character represented by one or more bytes encoded using UTF-8.
  • There are a few common pitfalls that developers may encounter when using CGO in Go.
    • Struct Alignment Issues
    • Memory Management
    • Type conversions
    • Complexity
    • Windows
  • You can turn C arrays into Go slices by using the reflect.SliceHeader struct and the unsafe package in GO.