头图

What is CGO?

CGO is a feature in the GO language. CGO belongs to the advanced usage of GOLANG, mainly through the use of GOLANG to call the program library implemented by CLANG

use

we can use

import "C" to use the CGO feature

One of the easiest CGOs to use

 package main


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

func main(){
    C.puts(C.CString("Hello, Cgo\n"))
}

import "C" can write the library C library that needs to be imported. It needs to be annotated. CGO will treat the comment content here as C code, which is called the preamble.

Functional explanation of the above code

Use the C.CString function of the CGO package to convert Go language strings to C language strings

Finally, call the C.puts function of the CGO package to print the converted C string to the standard output window

Use go build -x main.go compile

Add -x to print out the instructions executed during compilation

 # go build -x main.go
WORK=/tmp/go-build594331603
mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=/root/.cache/go-build/fb/fbb37eeb6735cb453f6d92e2e3f46f14d9dceb5baa1cdd10aae11d1d47d60e55-d
packagefile runtime/cgo=/usr/local/go/pkg/linux_amd64/runtime/cgo.a
packagefile syscall=/usr/local/go/pkg/linux_amd64/syscall.a
packagefile runtime=/usr/local/go/pkg/linux_amd64/runtime.a
packagefile errors=/usr/local/go/pkg/linux_amd64/errors.a
packagefile internal/bytealg=/usr/local/go/pkg/linux_amd64/internal/bytealg.a
packagefile internal/oserror=/usr/local/go/pkg/linux_amd64/internal/oserror.a
packagefile internal/race=/usr/local/go/pkg/linux_amd64/internal/race.a
packagefile internal/unsafeheader=/usr/local/go/pkg/linux_amd64/internal/unsafeheader.a
packagefile sync=/usr/local/go/pkg/linux_amd64/sync.a
packagefile internal/cpu=/usr/local/go/pkg/linux_amd64/internal/cpu.a
packagefile runtime/internal/atomic=/usr/local/go/pkg/linux_amd64/runtime/internal/atomic.a
packagefile runtime/internal/math=/usr/local/go/pkg/linux_amd64/runtime/internal/math.a
packagefile runtime/internal/sys=/usr/local/go/pkg/linux_amd64/runtime/internal/sys.a
packagefile internal/reflectlite=/usr/local/go/pkg/linux_amd64/internal/reflectlite.a
packagefile sync/atomic=/usr/local/go/pkg/linux_amd64/sync/atomic.a
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/linux_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=Vv0to6CWqbWf5_KTN66F/K36AEO-x4qJ_LJbz5wgG/HVbBbLSaW0sTSwlN8TzN/Vv0to6CWqbWf5_KTN66F -extld=gcc /root/.cache/go-build/fb/fbb37eeb6735cb453f6d92e2e3f46f14d9dceb5baa1cdd10aae11d1d47d60e55-d
/usr/local/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out main
rm -r $WORK/b001/

Try to write a C function yourself and let GO call it

Call this SayHello function in the Go locale

 package main

/*
#include <stdio.h>

static void SayHello(const char* s) {
    puts(s);
}
*/
import "C"

func main(){
    C.SayHello(C.CString("hello xiaomotong study cgo\n"))
}

Try to write a C file yourself, then import and call in GO

xmtC.h

 void SayHi(const char * str);

xmtC.c

(It must be a .c file in the same level directory. When cgo is compiled with go build, it will find the .c file in the same level directory by default for compilation. If we need to make the C file a static library Or the way of dynamic library, then do not put the source file of C in the same level directory to avoid duplicate names)

 #include <stdio.h>
#include "xmtC.h"

void SayHi(const char * str){
    puts(str);
}

main.go

 package main

//void SayHi(const char * str);
import "C"

func main(){
    C.SayHi(C.CString("hello xiaomotong study cgo\n"))
}

Run directly go build to compile and run the executable program

 # go build
# ls
cgo  main.go  xmtC.c
# ./cgo
hello xiaomotong study cgo

Through the programming technology oriented to the C language interface, we not only liberate the implementer of the function, but also simplify the user of the function. Now we can use SayHi as a standard library function (similar to how the puts function is used)

We can also write it like this in the go file

 package main

//#include <xmtC.h>
import "C"

func main(){
    C.SayHi(C.CString("hello xiaomotong study cgo\n"))
}

Merge C and GO code

In Go1.10 CGO adds a new one _GoString_ predefined C language type, used to represent Go language string

 // +build go1.10

package main

//void SayHi(_GoString_ s);
import "C"

import (
    "fmt"
)

func main() {
    C.SayHi("hello xiaomotong study cgo\n")
}

//export SayHi
func SayHi(s string) {
    fmt.Print(s)
}

The specific execution logic sequence of the above code is as follows:

CGO environment

The use of CGO requires certain environmental support

  • A compilation environment of gcc/g++ is required under linux
  • MinGW tools are required under windows
  • You need to set the environment variable of GO CGO_ENABLED to 1

In the above example, we have a few points to note:

  • import "C" The statement cannot be put together with other import statements, it needs to be placed on a separate line
  • The above values we pass in GO, such as C.CString("hello xiaomotong study cgo\n") are virtual packages that call C, convert strings into C strings and pass them in
  • Go is a strongly typed language

    Therefore, the parameter type passed in cgo must be exactly the same as the declared type, and the conversion function in "C" must be used to convert it into the corresponding C type before passing, and variables of the type in Go cannot be directly passed in.

    C symbols imported through virtual C packages do not need to start with an uppercase letter, they are not subject to Go's export rules

cgo usage

We can use the #cgo statement to set the relevant parameters of the compilation stage and the link stage

  • Compile phase parameters

Mainly used to define related macros and specify the header file retrieval path

  • Parameters for the linking phase

Mainly specify the library file retrieval path and the library file to be linked

For example we can

 // #cgo CFLAGS: -DPNG_DEBUG=1 -I./include
// #cgo LDFLAGS: -L/usr/local/lib -lpng
// #include <png.h>
import "C"

CFLAGS

  • -DPNG_DEBUG

Define the macro PNG_DEBUG, set to 1

  • -I

The search directory for defining the header file is ./include

LDFLAGS

  • -L

Specify the library file retrieval directory when linking, you can write ${SRCDIR} to indicate the absolute path of the current package

  • -l

Specify the library required for linking, here is the png library

Conditional compilation build tag

That is, when we go build , we add some conditional parameters. Of course, this conditional parameter needs to be in the corresponding file.

For example, when we used Go1.10 above, we added // +build go1.10 to the file

We can use it like this:

 go build -tags="debug"
go build -tags="debug test"
go build -tags="linux,386"

go build 的时候-tags参数,若有多个我们可以一起写,用空格 ,表示 ,用逗号 -Interval representation 逗号

Convert between GO and C data types

cgo officially provides the following data type conversions:

C language type CGO type Go language type
char C.char byte
singed char C.schar int8
unsigned char C.uchar uint8
short C.short int16
unsigned short C.ushort uint16
int C.int int32
unsigned int C.uint uint32
long C.long int32
unsigned long C.ulong uint32
long long int C.longlong int64
unsigned long long int C.ulonglong uint64
float C.float float32
double C. double float64
size_t C.size_t uint

3 points to note:

  • In CGO, the int and uint size_t long types of C language are corresponding to the memory size of 4 bytes. uint unsigned integer type treatment
  • In CGO, the C language int is fixed at 4 bytes in size, while the GO language int and uint are corresponding to 32-bit and 64-bit systems respectively. 4 bytes and 8 bytes in size
  • For example, there is a space in the data type, unsigned int cannot be accessed directly through C.unsigned int , you can use the typedef keyword to provide a regular type naming in CGO, which is more conducive to CGO access

String and slice types

Generated by CGO _cgo_export.h the header file, there are representations corresponding to data types such as strings, slices, channels, dictionaries, interfaces, etc. in GO, but we generally use strings and slices that are valuable.

Because CGO does not provide helper functions for other data types

 typedef struct { const char *p; GoInt n; } GoString;

When we export the function, we can write it like this:

Use the _GoString_ predefined type, which can reduce the risk of circular dependencies that may be generated in the cgo code to the _cgo_export.h header file

_GoString_ is a special character added by Go1.10 for Go

 extern void helloString(_GoString_ p0);

We can use the officially provided functions to calculate the length of the string and get the address of the string :

 size_t _GoStringLen(_GoString_ s);
const char *_GoStringPtr(_GoString_ s);

struct, union, enum

To access struct, union, enum of C language in GO language, you can view the corresponding relationship in the following table

C language GO language
struct xx C.struct_xx
union xx C.union_xx
enum xx C.enum_xx

for struct

The memory layout of the structure follows the general alignment rules of the C language

In the 32-bit Go locale, the C language structure also follows the 32-bit alignment rules, and in the 64-bit Go locale according to the 64-bit alignment rules

Structures with special alignment rules specified cannot be accessed in CGO

In GO, you can access C structs like this

 package main

/*
struct struct_TEST {
    int i;
    float f;
};
*/
import "C"
import "fmt"

func main() {
    var a C.struct_TEST
    a.i = 1
    a.f = 2

    fmt.Println(a.i)
    fmt.Println(a.f)
}

There are two major points to note:

  • The names of struct members are treated the same as the names of keywords in GO

For example, the above structure member name is like this

 struct struct_TEST {
    int type;
    float f;
};

Then when we access type, we can access it like this a._type

What if the structure is like this?

 struct struct_TEST {
    int type;
    float _type;
};

When we visited, we still visited like this, a._type , but the actual visit was float _type; , and there is no way to access int type; through GO

Zero-length arrays and bit fields in C are also not accessible in GO , e.g.

 struct struct_TEST {
    int   size: 10; // 位字段无法访问
    float arr[];    // 零长的数组无法访问
};
  • In the C language, there is no direct access to the struct type defined by the Go language

for enumeration enum

The bottom layer of the enumeration type corresponds to the int type, which supports negative type values. We can directly use C.xx to access

For example the enumeration type is:

 enum TEST {
    ONE,
    TWO,
};

Using this type we can use c C.enum_TEST

When copying this variable, we can do this: c = C.ONE

for the union

C language union types are not supported in Go language, they will be converted to byte arrays of the corresponding size

E.g

 union B1 {
    int i;
    float f;
};

union B1 will be converted to a 4-byte byte array [4]uint8

There are 3 ways to manipulate union variables in GO:

  • Defining Helper Functions in C Language
  • In Go language encoding/binary manually decode members (need to pay attention to big endian and little endian problem)
  • Use the unsafe package to cast to the corresponding type

for example

 package main

/*
#include <stdint.h>

union TEST {
    int i;
    float f;
};
*/
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    var b C.union_TEST

    *(*C.int)(unsafe.Pointer(&b)) = 1

    fmt.Println("b.i:", *(*C.int)(unsafe.Pointer(&b)))
    fmt.Println("b.f:", *(*C.float)(unsafe.Pointer(&b)))
}

When we read and write the union variables, we use unsafe the package performance is the best, get the pointer through unsafe , and then convert it to the pointer of the corresponding data type.

References:

GO Advanced Programming

Welcome to like, follow, favorite

Friends, your support and encouragement are the motivation for me to persist in sharing and improve quality

Okay, here it is this time

Technology is open, and our mentality should be open. Embrace change, live in the sun, and move forward.

I'm the little devil boy Nezha , welcome to like, follow and collect, see you next time~


阿兵云原生
192 声望37 粉丝