Command cgo

Cgo支持创建调用C代码的Go包.

Using cgo with the go command

要使用cgo,请编写导入伪包" C"的常规Go代码. 然后,Go代码可以引用诸如C.size_t之类的类型,诸如C.stdout之类的变量或诸如C.putchar之类的函数.

如果在" C"的导入后紧跟注释,则在编译程序包的C部分时,将该注释(称为前导)用作标头. 例如:

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

序言可以包含任何C代码,包括函数和变量的声明和定义. 然后可以从Go代码中引用这些内容,就好像它们是在包" C"中定义的一样. 可以使用在序言中声明的所有名称,即使它们以小写字母开头. 例外:前导中的静态变量可能无法从Go代码中引用; 允许使用静态函数.

有关示例,请参见$ GOROOT / misc / cgo / stdio和$ GOROOT / misc / cgo / gmp. 看到" C?去?Cgo!" 有关使用cgo的简介: https ://golang.org/doc/articles/c_go_cgo.html.

在这些注释中,可以使用伪#cgo指令定义CFLAGS,CPPFLAGS,CXXFLAGS,FFLAGS和LDFLAGS,以调整C,C ++或Fortran编译器的行为. 在多个指令中定义的值被串联在一起. 该指令可以包含一系列构建约束,这些约束将其作用限制在满足其中一个约束的系统上(有关约束语法的详细信息,请参阅https://golang.org/pkg/go/build/#hdr-Build_Constraints ). 例如:

// #cgo CFLAGS: -DPNG_DEBUG=1
// #cgo amd64 386 CFLAGS: -DX86=1
// #cgo LDFLAGS: -lpng
// #include <png.h>
import "C"

Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config tool using a '#cgo pkg-config:' directive followed by the package names. For example:

// #cgo pkg-config: png cairo
// #include <png.h>
import "C"

可以通过设置PKG_CONFIG环境变量来更改默认的pkg-config工具.

For security reasons, only a limited set of flags are allowed, notably -D, -I, and -l. To allow additional flags, set CGO_CFLAGS_ALLOW to a regular expression matching the new flags. To disallow flags that would otherwise be allowed, set CGO_CFLAGS_DISALLOW to a regular expression matching arguments that must be disallowed. In both cases the regular expression must match a full argument: to allow -mfoo=bar, use CGO_CFLAGS_ALLOW='-mfoo.*', not just CGO_CFLAGS_ALLOW='-mfoo'. Similarly named variables control the allowed CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS.

Also for security reasons, only a limited set of characters are permitted, notably alphanumeric characters and a few symbols, such as '.', that will not be interpreted in unexpected ways. 同样出于安全原因,只允许使用有限的一组字符,特别是字母数字字符和一些符号(例如"."),这些符号将不会以意想不到的方式进行解释. Attempts to use forbidden characters will get a "malformed #cgo argument" error. 尝试使用禁止的字符将得到"格式错误的#cgo参数"错误.

构建时,将CGO_CFLAGS,CGO_CPPFLAGS,CGO_CXXFLAGS,CGO_FFLAGS和CGO_LDFLAGS环境变量添加到从这些指令派生的标志中. 特定于软件包的标志应使用指令设置,而不是环境变量设置,以便在未修改的环境中构建工作. 从环境变量获得的标志不受上述安全限制的约束.

软件包中的所有cgo CPPFLAGS和CFLAGS伪指令被串联在一起,并用于编译该软件包中的C文件. 程序包中的所有CPPFLAGS和CXXFLAGS伪指令被串联在一起,并用于编译该程序包中的C ++文件. 程序包中的所有CPPFLAGS和FFLAGS伪指令被串联在一起,并用于编译该程序包中的Fortran文件. 程序中任何程序包中的所有LDFLAGS伪指令在链接时被串联并使用. 所有pkg-config伪指令被串联并同时发送到pkg-config,以添加到每个适当的命令行标志集.

解析cgo指令后,任何出现的字符串$ {SRCDIR}都将替换为包含源文件的目录的绝对路径. 这样可以将预编译的静态库包含在包目录中并正确链接. 例如,如果软件包foo位于/ go / src / foo目录中:

// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo

将扩展为:

// #cgo LDFLAGS: -L/go/src/foo/libs -lfoo

当Go工具看到一个或多个Go文件使用特殊的导入" C"时,它将在目录中查找其他非Go文件,并将其编译为Go包的一部分. 任何.c,.s或.S文件都将使用C编译器进行编译. 任何.cc,.cpp或.cxx文件都将使用C ++编译器进行编译. 任何.f,.F,.for或.f90文件都将使用fortran编译器进行编译. 任何.h,.hh,.hpp或.hxx文件都不会单独编译,但是,如果更改了这些头文件,则将重新编译该程序包(包括其非Go源文件). 请注意,对其他目录中文件的更改不会导致重新编译软件包,因此,该软件包的所有非Go源代码都应存储在软件包目录中,而不是子目录中. 默认的C和C ++编译器可以分别通过CC和CXX环境变​​量进行更改. 这些环境变量可能包括命令行选项.

默认情况下,对于预期在其上运行的系统上的本机版本,将启用cgo工具. 交叉编译时默认情况下禁用它. 您可以通过在运行go工具时设置CGO_ENABLED环境变量来控制它:将其设置为1以启用cgo,将其设置为0以禁用它. 如果启用了cgo,go工具将设置构建约束" cgo". 特殊导入" C"表示" cgo"构建约束,就好像该文件还说" // + build cgo"一样. 因此,如果禁用了cgo,则go工具将不会构建导入" C"的文件. (有关构建约束的更多信息,请参见https://golang.org/pkg/go/build/#hdr-Build_Constraints ).

交叉编译时,必须指定C交叉编译器供cgo使用. 您可以通过在使用make.bash构建工具链时设置通用CC_FOR_TARGET或更具体的CC_FOR _ $ {GOOS} _ $ {GOARCH}环境变量(例如CC_FOR_linux_arm)来完成此操作,也可以随时设置CC环境变量您运行go工具.

CXX_FOR_TARGET,CXX_FOR _ $ {GOOS} _ $ {GOARCH}和CXX环境变​​量对于C ++代码的工作方式相似.

Go references to C

在Go文件中,可以在Go的关键字中加下划线来访问作为Go中关键字的C的结构字段名称:如果x指向具有名为" type"的字段的C结构,则x._type将访问该字段. 无法在Go中表示的C结构字段(例如位字段或未对齐的数据)在Go结构中被省略,由适当的填充替换以到达下一个字段或结构的末尾.

标准C数值类型的名称为C.char,C.schar(有符号字符),C.uchar(无符号字符),C.short,C.ushort(无符号short),C.int,C.uint( unsigned int),C.long,C.ulong(无符号long),C.longlong(长long),C.ulonglong(无符号long long),C.float,C.double,C.complexfloat(复杂浮点)和C.complexdouble(复杂双精度). C类型的void *由Go的unsafe.Pointer表示. C类型__int128_t和__uint128_t用[16] byte表示.

在Go中通常由指针类型表示的一些特殊C类型由uintptr表示. 请参阅下面的特殊情况部分.

要直接访问struct,union或enum类型,请像在C.struct_stat中一样,为其前缀struct_,union_或enum_.

像C.sizeof_struct_stat一样,任何C类型T的大小都可以作为C.sizeof_T获得.

可以在Go文件中使用特殊名称_GoString_的参数类型声明AC函数. 可以使用普通的Go字符串值调用此函数. 可以通过调用C函数来访问字符串长度和指向字符串内容的指针

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

这些功能仅在序言中可用,而在其他C文件中不可用. C代码不得修改_GoStringPtr返回的指针的内容. 请注意,字符串内容可能没有尾随的NUL字节.

由于Go通常不支持C的联合类型,因此C的联合类型表示为具有相同长度的Go字节数组.

Go结构无法嵌入具有C类型的字段.

Go代码不能引用出现在非空C结构末尾的零大小字段. 要获取此类字段的地址(这是对大小为零的字段可以执行的唯一操作),您必须获取结构的地址并添加结构的大小.

Cgo将C类型转换为等效的未导出Go类型. 由于未导出翻译,因此Go程序包不应在其导出的API中公开C类型:一个Go程序包中使用的C类型与另一个Go程序包中使用的C类型不同.

可以在多重赋值上下文中调用任何C函数(甚至是void函数),以将返回值(如果有)和C errno变量作为错误进行检索(如果函数返回void,请使用_跳过结果值). 例如:

n, err = C.sqrt(-1)
_, err := C.voidFunc()
var n, err = C.sqrt(1)

当前不支持调用C函数指针,但是您可以声明Go变量,该变量包含C函数指针,并将它们在Go和C之间来回传递.C代码可能调用从Go接收到的函数指针. 例如:

package main

// typedef int (*intFunc) ();
//
// int
// bridge_int_func(intFunc f)
// {
//		return f();
// }
//
// int fortytwo()
// {
//	    return 42;
// }
import "C"
import "fmt"

func main() {
	f := C.intFunc(C.fortytwo)
	fmt.Println(int(C.bridge_int_func(f)))
	// Output: 42
}

在C语言中,写为固定大小数组的函数参数实际上需要一个指向数组第一个元素的指针. C编译器知道此调用约定并相应地调整了调用,但是Go不能. 在Go中,必须将指针显式传递到第一个元素:Cf(&C.x [0]).

不支持调用可变参数C函数. 通过使用C函数包装器可以避免这种情况. 例如:

package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
//   printf("%s\n", s);
// }
import "C"
import "unsafe"

func main() {
	cs := C.CString("Hello from stdio")
	C.myprint(cs)
	C.free(unsafe.Pointer(cs))
}

一些特殊功能通过复制数据在Go和C类型之间进行转换. 在伪Go定义中:

// Go string to C string
// The C string is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CString(string) *C.char

// Go []byte slice to C array
// The C array is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CBytes([]byte) unsafe.Pointer

// C string to Go string
func C.GoString(*C.char) string

// C data with explicit length to Go string
func C.GoStringN(*C.char, C.int) string

// C data with explicit length to Go []byte
func C.GoBytes(unsafe.Pointer, C.int) []byte

作为一种特殊情况,C.malloc不会直接调用C库malloc,而是会调用Go帮助程序函数,该函数包装C库malloc但保证永不返回nil. 如果C的malloc指示内存不足,则辅助函数会使程序崩溃,就像Go本身内存不足时一样. 因为C.malloc不会失败,所以它没有返回errno的两个结果的形式.

C references to Go

Go函数可以通过以下方式导出以供C代码使用:

//export MyFunction
func MyFunction(arg1, arg2 int, arg3 string) int64 {...}

//export MyFunction2
func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...}

它们将以C代码形式提供:

extern GoInt64 MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);

从cgo输入文件复制的任何序言之后,在_cgo_export.h生成的标头中找到. 具有多个返回值的函数映射到返回结构的函数.

并非所有的Go类型都能以一种有用的方式映射到C类型. 不支持Go结构类型; 使用C结构类型. 不支持Go数组类型; 使用C指针.

如上所述,可以使用C类型_GoString_调用采用字符串类型参数的Go函数. _GoString_类型将在序言中自动定义. 请注意,C代码无法创建这种类型的值. 这仅适用于将字符串值从Go传递到C,然后再传递回Go的情况.

在文件中使用// export会限制前导:由于将其复制到两个不同的C输出文件中,因此它不得包含任何定义,而只能包含声明. 如果文件同时包含定义和声明,则两个输出文件将产生重复的符号,并且链接器将失败. 为避免这种情况,必须将定义放在其他文件或C源文件的前言中.

Passing pointers

Go是一种垃圾回收语言,垃圾回收器需要知道每个Go内存指针的位置. 因此,在Go和C之间传递指针受到限制.

在本节中,术语Go指针表示指向Go分配的内存的指针(例如通过使用&运算符或调用预定义的新函数),术语C指针表示指向C分配的内存的指针(例如通过调用C.malloc). 指针是Go指针还是C指针是由内存分配方式决定的动态属性. 它与指针的类型无关.

请注意,某些Go类型的值(该类型的零值除外)始终包含Go指针. 字符串,片段,接口,通道,映射和函数类型都是如此. 指针类型可以包含Go指针或C指针. 数组和结构类型可能包括也可能不包括Go指针,具体取决于元素类型. 下面有关Go指针的所有讨论不仅适用于指针类型,还适用于其他包含Go指针的类型.

如果Go代码所指向的Go存储器不包含任何Go指针,则Go代码可以将Go指针传递给C. C代码必须保留此属性:即使在临时状态下,也不得在Go内存中存储任何Go指针. 当将指针传递到结构中的字段时,所讨论的Go内存是该字段占用的内存,而不是整个结构. 当将指针传递到数组或切片中的元素时,所讨论的Go内存是切片的整个数组或整个后备数组.

调用返回后,C代码可能不会保留Go指针的副本. 这包括_GoString_类型,如上所述,该类型包括Go指针; _GoString_值可能不会被C代码保留.

由C代码调用的Go函数可能不会返回Go指针(这意味着它可能不会返回字符串,片段,通道等). 由C代码调用的Go函数可以将C指针作为参数,并且可以通过这些指针存储非指针或C指针数据,但不能在由C指针指向的内存中存储Go指针. 由C代码调用的Go函数可以将Go指针作为参数,但是它必须保留其指向的Go存储器不包含任何Go指针的属性.

Go代码可能不会在C内存中存储Go指针. 根据上述规则,C代码可能会将Go指针存储在C内存中:当C函数返回时,它必须停止存储Go指针.

这些规则在运行时动态检查. 该检查由GODEBUG环境变量的cgocheck设置控制. 默认设置为GODEBUG = cgocheck = 1,它实现了相当便宜的动态检查. 可以使用GODEBUG = cgocheck = 0完全禁用这些检查. 可以通过GODEBUG = cgocheck = 2获得对指针处理的完整检查,而这需要花费一些运行时间.

使用不安全的程序包可能会破坏这种强制执行,当然,没有什么可以阻止C代码做任何喜欢的事情. 但是,违反这些规则的程序可能会以意外和不可预测的方式失败.

注意:当前的实现存在一个错误. 尽管允许Go代码向C存储器写入nil或C指针(而不是Go指针),但是如果C存储器的内容似乎是Go指针,则当前实现有时可能会导致运行时错误. 因此,如果Go代码要在其中存储指针值,请避免将未初始化的C内存传递给Go代码. 将C中的内存清零,然后再传递给Go.

Special cases

在Go中通常由指针类型表示的一些特殊C类型由uintptr表示. 这些包括:

1. The *Ref types on Darwin, rooted at CoreFoundation's CFTypeRef type.

2.来自Java的JNI接口的对象类型:

jobject
jclass
jthrowable
jstring
jarray
jbooleanArray
jbyteArray
jcharArray
jshortArray
jintArray
jlongArray
jfloatArray
jdoubleArray
jobjectArray
jweak

3. EGL API中的EGLDisplay类型.

这些类型在Go方面是uintptr,因为它们否则会混淆Go垃圾收集器. 它们有时不是真正的指针,而是以指针类型编码的数据结构. 对这些类型的所有操作都必须在C中进行.初始化一个空的此类引用的适当常数为0,而不是nil.

这些特殊情况在Go 1.10中引入. 要从Go 1.9和更低版本自动更新代码,请在Go修复工具中使用cftype或jni重写:

go tool fix -r cftype <pkg>
go tool fix -r jni <pkg>

它将在适当的位置将nil替换为0.

Go 1.12中引入了EGLDisplay盒. 使用egl重写从Go 1.11和更早版本自动更新代码:

go tool fix -r egl <pkg>

Using cgo directly

Usage:

go tool cgo [cgo options] [-- compiler options] gofiles...

Cgo将指定的输入Go源文件转换为多个输出Go和C源文件.

调用C编译器来编译软件包的C部分时,编译器选项将通过未解释的方式传递.

直接运行cgo时,以下选项可用:

-V
	Print cgo version and exit.
-debug-define
	Debugging option. Print #defines.
-debug-gcc
	Debugging option. Trace C compiler execution and output.
-dynimport file
	Write list of symbols imported by file. Write to
	-dynout argument or to standard output. Used by go
	build when building a cgo package.
-dynlinker
	Write dynamic linker as part of -dynimport output.
-dynout file
	Write -dynimport output to file.
-dynpackage package
	Set Go package for -dynimport output.
-exportheader file
	If there are any exported functions, write the
	generated export declarations to file.
	C code can #include this to see the declarations.
-importpath string
	The import path for the Go package. Optional; used for
	nicer comments in the generated files.
-import_runtime_cgo
	If set (which it is by default) import runtime/cgo in
	generated output.
-import_syscall
	If set (which it is by default) import syscall in
	generated output.
-gccgo
	Generate output for the gccgo compiler rather than the
	gc compiler.
-gccgoprefix prefix
	The -fgo-prefix option to be used with gccgo.
-gccgopkgpath path
	The -fgo-pkgpath option to be used with gccgo.
-godefs
	Write out input file in Go syntax replacing C package
	names with real values. Used to generate files in the
	syscall package when bootstrapping a new target.
-objdir directory
	Put all generated files in directory.
-srcdir directory

by  ICOPY.SITE