This is the 29th day of my participation in the August Wenwen Challenge.More challenges in August
CGO learning 1, preliminary cognition and basic data type conversion
What is CGO?
CGO is a feature of the GO language. CGO is a high-level use of GOLANG, mainly through the use of GOLANG call CLANG implementation of the library
use
We can use
Import “C” to use the CGO feature
One of the simplest CGO to use
package main
//#include <stdio.h>
import "C"
func main(a){
C.puts(C.CString("Hello, Cgo\n"))}Copy the code
Above the import “C” you can write the library that you want to import. The C library needs to be commented out, and CGO will regard this comment content as C code, which is called preamble.
A functional explanation of the above code
Convert a Go language String to a C language String using the CString function of the CGO package
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 to compile
Plus -x prints 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/
Copy the code
Try writing a C function yourself and letting GO call it
The SayHello function is called in the Go context
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"))
}
Copy the code
Try writing a C file yourself and importing and calling it in GO
xmtC.h
void SayHi(const char * str);
Copy the code
xmtC.c
C files must be in the same directory, cgo use go build, will default to the same directory to find. C files for compilation, if we need to make c files into static libraries or dynamic libraries, then do not put c source files in the same directory, avoid the same name.
#include <stdio.h>
#include "xmtC.h"
void SayHi(const char * str){
puts(str);
}
Copy the code
main.go
package main
//void SayHi(const char * str);
import "C"
func main(a){
C.SayHi(C.CString("hello xiaomotong study cgo\n"))}Copy the code
Run go Build directly to compile and run the executable
# go build
# ls
cgo main.go xmtC.c
# ./cgo
hello xiaomotong study cgo
Copy the code
Through C interface oriented programming technology, we not only free the implementers of functions, but also simplify the users of functions. Now we can use SayHi as a library function (similar to the puts function)
We could also write it like this in the GO file
package main
//#include <xmtC.h>
import "C"
func main(a){
C.SayHi(C.CString("hello xiaomotong study cgo\n"))}Copy the code
Merge C and GO code
In Go1.10, CGO added a _GoString_ predefined C type to represent the Go language string
/ / + build go1.10
package main
//void SayHi(_GoString_ s);
import "C"
import (
"fmt"
)
func main(a) {
C.SayHi("hello xiaomotong study cgo\n")}//export SayHi
func SayHi(s string) {
fmt.Print(s)
}
Copy the code
The logical order of execution is as follows:
CGO environment
Using CGO requires certain environmental support
- This is required for Linux
gcc/g++
Compiler environment - Windows requires MinGW tools
- You need to put the environment variable of GO
CGO_ENABLED
Setting 1
There are a few things to note in the above example:
-
Import “C” statements cannot be placed with other import statements and must be placed on a separate line
-
CString(” Hello xiaomotong study cgo\n”) is a virtual package that calls C to convert a String into a CString
-
Go is a strongly typed language
Therefore, the type of the parameter passed in CGO must be exactly the same as the declared type, and must be converted into the corresponding C type by the conversion function in “C” before transmission. Variables of the type in Go cannot be directly passed in
C symbols imported through virtual C packages do not need to start with a capital letter and are not subject to Go’s export rules
# cgo usage
We can use the #cgo statement to set parameters for the compile and link phases
- Parameters for compile time
It is used to define related macros and specify header file retrieval paths
- Parameters for the link stage
Basically, you specify the library file retrieval path and the library file to link to
For example, we could do this
// #cgo CFLAGS: -DPNG_DEBUG=1 -I./include
// #cgo LDFLAGS: -L/usr/local/lib -lpng
// #include <png.h>
import "C"
Copy the code
CFLAGS
- -DPNG_DEBUG
Define the macro PNG_DEBUG, set to 1
- -I
The retrieval directory for defining header files is./include
LDFLAGS
- -L
Specify the directory to retrieve library files when linking. You can write ${SRCDIR} to indicate the absolute path of the current package
- -l
The library needed to specify the link, in this case the PNG library
Conditional build tag
When we go build, we add some conditional parameters. Of course, this conditional parameter is required 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"
Copy the code
If there are more than one tags, we can write them together, using Spaces to indicate or, and using commas to indicate and
GO and C data types are converted to each other
Cgo officially provides the following data type conversions:
C language type | CGO type | Go language types |
---|---|---|
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 |
Three points to note:
-
In CGO, C int and long correspond to a 4-byte memory size, and size_t can be treated as a Uint unsigned integer in Go
-
In CGO, THE int of C language is fixed at 4 bytes, while the int and uint of GO language correspond to 4 bytes and 8 bytes respectively in 32-bit and 64-bit systems
-
For example, if a datatype has Spaces in the middle and an unsigned int cannot be accessed directly through c. unsigned int, you can use the typedef keyword to provide a regular type name that is easier to access in CGO
String and slice type
The _cGO_export. h header file generated by CGO contains representations of data types such as strings, slices, channels, dictionaries and interfaces in GO, but we generally use strings and slices as valuable representations
Because CGO does not provide helper functions for other data types
typedef struct { const char *p; GoInt n; } GoString;
Copy the code
When we export the function, we can write:
Use the _GoString_ predefined type, which can be written to reduce the risk of cyclic dependencies on the _cGO_export.h header file in CGO code
_GoString_ is a character added to Go1.10 specifically for Go
extern void helloString(_GoString_ p0);
Copy the code
We can calculate the length of the string and get the address of the string using the function provided by the official:
size_t _GoStringLen(_GoString_ s);
const char *_GoStringPtr(_GoString_ s);
Copy the code
Struct, union, enum
Struct, Union, and enum of C language are accessed in GO language, and the corresponding relationship can be viewed in the following table
The C language | The GO |
---|---|
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 common alignment rules of C
C constructs also follow the 32-bit alignment rules in the 32-bit Go environment and the 64-bit alignment rules in the 64-bit Go environment
Structures that specify special alignment rules cannot be accessed in CGO
The structure of C can be accessed in GO in this way
package main
/* struct struct_TEST { int i; float f; }; * /
import "C"
import "fmt"
func main(a) {
var a C.struct_TEST
a.i = 1
a.f = 2
fmt.Println(a.i)
fmt.Println(a.f)
}
Copy the code
Note the following two points:
- The names of structure members are treated the same as the names of keywords in GO
For example, the structure member name looks like this
struct struct_TEST {
int type;
float f;
};
Copy the code
When we access type, we can access a._type like this
What if the structure looks like this?
struct struct_TEST {
int type;
float _type;
};
Copy the code
We are still accessing this, a._type, but we are actually accessing float _type; , there is no way to access int type through GO;
Zero-length arrays and bit fields in C are also not accessible in GO, for example
struct struct_TEST { int size: 10; // Bit fields cannot access float arr[]; // a zero-length array cannot be accessed};Copy the code
- In C, there is no direct access to the struct types defined by Go
For enumeration enum
The enumeration type corresponds to the underlying int, and supports negative values, which can be accessed directly using C.x
For example, the enumeration type is:
enum TEST {
ONE,
TWO,
};
Copy the code
With this type we can use c.ennum_test
To copy this variable, we can do this: c = c.ne
For union union
C union types are not supported in Go; they are converted to arrays of bytes of corresponding size
For example,
union B1 {
int i;
float f;
};
Copy the code
Union B1 is converted into a 4 byte size byte array [4]uint8
There are three ways to manipulate union variables in GO:
- Define helper functions in C
- The Go
encoding/binary
Manually decode members **(be aware of big end and small end issues)** - use
unsafe
Package forces a 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)))
}
Copy the code
The unsafe package performs best when reading and writing union variables, where Pointers are retrieved and then converted to the corresponding data type
References:
GO Advanced Programming
Welcome to like, follow and favorites
Friends, your support and encouragement, I insist on sharing, improve the quality of the power
All right, that’s it for this time
Technology is open, our mentality, should be more open. Embrace change, live in the sun, and strive to move forward.
I am Nezha, welcome to like, see you next time ~