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 stringsFinally, 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
anduint
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 languageint
anduint
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 throughC.unsigned int
, you can use thetypedef
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:
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~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。