There are specific scenarios where we are troubled by the fact that

  1. the performance of the current development language is still insufficient, such as video processing (in the field of live streaming), machine learning, and games.
  2. there are some excellent C/C++ libraries that cannot be reimplemented in the current development language for a while (FFmpeg, OpenCV, Protobuf, ZeroMQ, and a whole lot more).

In general, we will tend to use several ways to solve this.

  1. encapsulating the original C/C++ library and turning the interfaces therein into those of the current language.
  2. developing C/C++ Web services, which is also equivalent to encapsulating the original library, except that it is encapsulated into a Web API interface.

The idea here is to add a “glue layer” to “glue” the caller and the called together.


So, we can see that in Node.js, there is a very convenient way to integrate addons, which can be called directly after being wrapped and introduced by means of require. However, because of the need to compile for the platform, every time Node.js is upgraded, it is easy to cause the module to be broken, Node.js until 8.0, to provide N-API to ensure that when Node itself is upgraded to a large version, the extended C/C++ module can still be used. (DIP again, by the way. By relying on abstractions rather than concrete implementations, this problem is avoided.)

Also, a few of our commonly used C/C++ modules can be mentioned in passing.

  1. gRPC: communication
  2. iconv: character set conversion
  3. canvas: server-side canvas elements

Node.js addons are not much studied, so I’ll talk about them later when I get a chance.


In Golang, we have to mention cgo, which seems to have more black magic in it than the Node.js addon.

First, the way to call C code in Go is like this.

// #include <stdio.h>
// #include <errno.h>
import "C"

Then all C code can be called by C.XXX like C.putchar, C.malloc, and so on.

Here, you should be able to see that the imported C package is a “pseudo-package”, which is a kind of black magic, because it is treated as a namespace for cgo.

The key thing is that most of the base types in C and Go are convertible to each other, unlike Node.js or Python, which need to be wrapped in a special dynamic language object. cgo maps various data types, such as C int to go int or int32, C float to Go float32, and so on.

Here are two examples that are special and that we may use frequently.

CGO struct

As for structs in C, you can use C.struct_example to define them, but also note that if you use C’s packed struct, you need special treatment.

CGO struct array

Returning a struct array in C is a common requirement, and we can handle it in two ways.

In the case where the length is known.

exampleSize := 10
examples := C.get_structs()
exampleSlice := (*[1 << 30]C.struct_Example)(unsafe.Pointer(examples))[:exampleSize:exampleSize]

Not known length, dynamic struct length, similar to above.

var examples *C.struct_Example
var size C.size_t
C.get_structs((**C.struct_Example)(unsafe.Pointer(&examples)), (*C.size_t)(unsafe.Pointer(&size)))
exampleSlice := (*[1 << 30]C.struct_Example)(unsafe.Pointer(examples))[:size:size]

CGO compilation

The compilation of CGO is also black magic and requires the relevant compilation options to be written in the comments.

// #cgo CFLAGS: -I${SRCDIR}/include
// #cgo LDFLAGS: -L${SRCDIR}/lib -lfoo

You can also use pkg-config if you are introducing other libraries.

// #cgo pkg-config: opencv

Also, since compilation options may be different for different platforms, then compilation restrictions can be added.

// #cgo darwin,amd64 LDFLAGS: -lomp
// #cgo linux,amd64  LDFLAGS: -lgomp

where a comma can be seen as and and a space indicates or, see here for details.

Sample Examples

If you want to learn more from specific examples, you can refer to the following projects.