O comando Cgo permite a criação de pacotes Go que utilizam de código C.
Usando o comando cgo com o comando go
Para usar o comando cgo escreve um código Go normal que importe o pseudo-pacote “C”. O código Go poderá assim fazer referência a tipos como o C.size_t, variáveis como C.stdout, ou funções como C.putchar.
Se a importação de “C” for precedida imediatamente por um comentário, esse comentário, chamado de preâmbulo, é usado como cabeçalho para compilar as partes em C do pacote. Por exemplo:
// #include <stdio.h> // #include <errno.h> import "C"
O preâmbulo pode conter qualquer código C, incluindo declarações e definições de funções e variáveis. Elas podem ser referenciadas no código Go da mesma forma que foram definidas no pacote “C”. Todos os nomes declarados no preâmbulo podem ser usados, mesmo se forem iniciados com uma letra minuscula. Exceção: variáveis estáticas no preâmbulo não podem ser referenciadas no código Go; funções estáticas são permitidas.
Veja $GOROOT/misc/cgo/stdio e $GOROOT/misc/cgo/gmp para exemplos de uso. Veja o artigo “C? Go? Cgo!” para uma introduão no uso do comando cgo: https://golang.org/doc/articles/c_go_cgo.html.
Macros CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS e LDFLAGS podem ser definidas com pseudo-diretivas #cgo nesses comentários para fazer ajustes no comportamento do compilador C, C++ ou Fortran. Valores definidos em múltiplas diretivas são concatenados todos juntos. A diretiva pode incluir uma lista de restrições de compilação que limitam seu efeito em sistemas que satisfazem a restrição (Veja https://golang.org/pkg/go/build/#hdr-Build_Constraints para detalhes sobre a sintaxe dessas restrições). Por exemplo:
// #cgo CFLAGS: -DPNG_DEBUG=1 // #cgo amd64 386 CFLAGS: -DX86=1 // #cgo LDFLAGS: -lpng // #include <png.h> import "C"
De forma alternativa, CPPFLAGS e LDFLAGS podem ser obtidas via ferramenta pkg-config usando uma diretiva ‘#cgo pkg-config:’ seguida pelos nomes do pacote: Por exemplo:
// #cgo pkg-config: png cairo // #include <png.h> import "C"
Ao compilar CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS e CGO_LDFLAGS, as variáveis de ambiente são adicionadas às flags derivadas dessas diretivas. Flags especificas do pacote devem ser definidas usando as diretivas, não variáveis de ambiente, de forma que a compilação funcione em ambientes não modificados.
Todas as diretivas cgo CPPFLAGS e CFLAGS de um pacote são concatenadas e usadas para compilar arquivos C desse pacote. Todas as diretivas CPPFLAGS e CXXFLAGS do pacote são concatenadas e usadas para compilar arquivos C++ do pacote. Todas as diretivas LDFLAGS em um pacote são concatenadas e usadas durante o processo de ligacão com bibliotecas externas. Todas as diretivas pkg-config são concatenadas e enviadas para o utilitário pkg-config simultaneamente para adicionar cada conjunto apropriado de flags de linha comando.
Quando as diretivas cgo são processadas, qualquer ocorrência da string ${SRCDIR} será substituida pelo caminho absoluto para o diretório que contém o código fonte. Isso permite que bibliotecas estáticas pré-compiladas sejam incluídas no diretório do pacote e ligadas de forma apropriadas. Por exemplo, se o pacote foo estiver no diretório /go/src/foo:
// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
Será expandido para:
// #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
Quando a ferramenta Go enxerga um ou mais arquivos Go que usem o import especial “C”, irá procurar por outros arquivos não Go no diretório e irá compila-los como parte do pacote Go. Qualquer arquivo *.c, *.s ou *.S será compilado com o compilador C. Arquivos *.cc, *.cpp ou *.cxx serão compilador com o compilador C++. Qualquer arquivo *.f, *.F, *.for ou *.f90 serão compilador com o compilador Fortran. Qualquer arquivo *.h, *.hh, *.hpp, ou *.hxx não serão compilados separadamente, mas se eles foram alterados os arquivos C ou C++ serão recompilados. Os compiladores padrão C e C++ podem ser alterados alterando as variáveis de ambiente CC e CXX, respectivamente; essas variáveis de ambiente podem incluir opções de linha de comando.
A ferramenta cgo é ativa por padrão para compilações nativas em sistemas onde se espera que funcione. É desativada por padrão no caso de compilação cruzada ( cross-compiling). Você pode controlar isso ajustando a variável de ambiente CGO_ENABLED ao executar a ferramenta Go: ajuste para 1 para ativar o uso de cgo, e para 0 para desativar. A ferramenta Go irá configurar a opção de compilação cgo se estiver ativada.
Quando estiver executando uma compilação cruzada, você precisa especificar um compilador cruzado C para o cgo usar. Você pode fazer isso pelo ajuste da variável de ambiente CC_FOR_TARGET quando estiver compilando a cadeia usando make.bash, ou ajustando a variável de ambiente CC toda vez que for executar a ferramenta Go. As variáveis de ambiente CXX_FOR_TARGET e CXX funcionam de forma similar para código C++.
Referências Go para C
Dentro do arquivo Go, nomes de campos de structs de C que sejam palavras chave Go podem ser acessadas prefixando elas com um traco: se x aponta para uma struct de C com campo de nome type, x._type irá acessar esse campo. Campos de structs que não podem ser expressados em Go, como campos de bits ou dados desalinhados, são omitidos na struct de Go, e substituídos pelo espaçamento apropriado para seja alcançado o próximo campo ou o final da struct.
Os tipos numéricos padrão de C estão disponíveis sob os nomes C.char, C.schar (signed char), C.uchar (unsigned char), C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int), C.long, C.ulong (unsigned long), C.longlong (long long), C.ulonglong (unsigned long long), C.float, C.double, C.complexfloat (complex float), e C.complexdouble (complex double). O tipo C void* é representado em Go por unsafe.Pointer. Os tipos __int128_t e __uint128_t são representados em Go por [16]byte.
Para acessar uma struct, union ou enum diretamente, use os prefixos struct_, union_, or enum_, como em C.struct_stat.
O tamanho de qualquer tipo C como T está disponível como C.sizeof_T, por exemplo C.sizeof_struct_stat.
Como Go não tem suporte para tipos union de C no geral, tipos union são representados por arrays de byte Go com o mesmo tamanho.
As structs de Go não podem ter campos com tipos de C.
Código Go não pode ter referências a campos de tamanho zero que ocorrem no final de structs C não vazias. Para obter o endereço desse tipo de campo (que é a única operação que pode ser feita com um campo de tamanho zero) você precisa obter o endereço da struct e somar o tamanho dela.
O comando cgo traduz tipos do C em tipos do Go não exportados equivalentes. Como a tradução não é exportada, um pacote Go não deve expor tipos C em sua API exportada: um tipo C usado em um pacote Go é diferente do mesmo tipo C usado em outro pacote.
Qualquer função C (mesmo funções void) podem ser chamadas em contexto de múltiplas atribuições para ler tanto o valor de retorno (se houver) e a mensagem de erro C (use _ para ignorar o valor de retorno se a função retorna void). Por exemplo:
n, err = C.sqrt(-1) _, err := C.voidFunc() var n, err = C.sqrt(1)
Chamada para ponteiros para funções C não é suportada atualmente, porém você pode declarar variáveis Go que armazenem ponteiros para funções C e passa-las adiante entre Go e C. O código C poderá chamar ponteiros de funções recebidas de Go. Por exemplo:
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 }
Em C, um argumento de função escrito como um array de tamanho fixo na verdade requer um ponteiro para o primeiro elemento do array. Compiladores C estão cientes dessa convenção e ajustam a chamada de acordo, mas não Go. EM Go, você precisa passar o ponteiro parta o primeiro elemento explicitamente: C.f(&C.x[0]).
Algumas funções especiais fazem a conversão entre tipos Go e C fazendo cópias dos dados. Em definições pseudo-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
Referências C para Go
Funções Go podem ser exportadas para serem usadas no código C da seguinte forma:
//export MyFunction func MyFunction(arg1, arg2 int, arg3 string) int64 {...} //export MyFunction2 func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...}
Elas ficarão disponíveis no código C como:
extern int64 MyFunction(int arg1, int arg2, GoString arg3); extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);
encontradas no arquivo de cabeçalho geral _cgo_export.h, depois de qualquer preâmbulo copiado dos arquivo de entrada do cgo. Funções com múltiplos valores de retorno são mapeadas com funções que retornam uma struct. Nem todos os tipos Go podem ser mapeados para tipos C de uma maneira útil.
Usar //export em um arquivo poe uma restrição no preâmbulo: como é copiada em dois diferentes arquivos de saída C, não pode conter definições, somente declarações. Se um arquivo possuir definições e declarações, os dois arquivos de saída produzirão símbolos duplicadas e o linker irá gerar um erro. Para evitar isso, definições devem ser colocadas em preâmbulos de outros arquivos, ou nos arquivos de código fonte C.
Usando ponteiros
A linguagem Go usa um coletor de lixo, e ele precisa saber a localização de todos os ponteiros da memória usada pelo Go. Por causa disso, existem restrições ao se utilizar ponteiros entre Go e C.
Nessa seção, o termo ponteiro Go significa um ponteiro para memória alocada por Go (como pelo uso do operador & ou chamando a função pre-definida new) e o termo ponteiro C significa um ponteiro para memória alocada por C (como por uma chamada para C.malloc). Ser um ponteiro Go ou um ponteiro C é uma propriedade dinâmica determinada por como a memória foi alocada; não tem relação com o tipo do ponteiro.
O código Go pode passar um ponteiro Go para o código C desde que a memória Go para onde ele aponta não contenha um ponteiro Go. O código C deve preservar essa propriedade: não pode armazenar nenhum ponteiro Go na memória Go, mesmo temporariamente. Quando for passar um ponteiro para um campo de uma struct, a memória Go em questão é a memória ocupada pelo campo, não pela struct inteira. Quando estiver passando um ponteiro para um elemento de um array ou slice, a memória Go em questão é o array inteiro.
O código C não pode manter uma cópia de um ponteiro Go depois do retorno da função.
Uma função Go chamada pelo código C não pode retorna um ponteiro Go. Uma função Go chamada pelo código C pode ter ponteiros como argumentos, e pode armazenar dados não ponteiros ou ponteiros C desses ponteiros, mas não pode armazenar um ponteiro Go na memória apontada por um ponteiro C. Uma função Go chamada pelo código C pode usar um ponteiro Go como argumento, mas deve preservar a propriedade de que essa memória não contenha ponteiros Go.
Código Go não pode armazenar um ponteiro Fo em memória C. Código C pode armazenar ponteiros Go em memória C, sujeita à regra acima: precisa parar de armazenar o ponteiro Go quando a função C retorna um valor.
Essas regras são verificadas dinamicamente durante a execução. A verificação é controlada por cgocheck ajustando a variável de ambiente GODEBUG. O ajuste padrão é GODEBUG=cgocheck=1, que implementa uma checagem dinâmica razoavelmente de baixo custo. Essas checagens podem ser desativadas totalmente usando-se GODEBUG=cgocheck=0. Uma checagem completa da manipulação de ponteiros, com algum custo na execução, está disponível com GODEBUG=cgocheck=2.
É possível evitar esse reforço usando o pacote inseguro, e naturalmente com isso nada evita que o código C de fazer o que quiser. Porém, programas que quebram essas regras tem grande chances de erros de formas inesperadas e imprevisíveis.
Usando o comando cgo diretamente
Uso:
go tool cgo [cgo options] [-- compiler options] gofiles...
O comando cgo transforma arquivos Go especificados em vários arquivos de código Go e C.
As opções de compilação são passadas não interpretadas quando o compilador C é invocado para compilar as partes em C do pacote.
As seguintes opções estão disponíveis quando se executa o comando cgo diretamente:
-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. -dynout file Write -dynimport output to file. -dynpackage package Set Go package for -dynimport output. -dynlinker Write dynamic linker as part of -dynimport output. -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. -importpath string The import path for the Go package. Optional; used for nicer comments in the generated files. -exportheader file If there are any exported functions, write the generated export declarations to file. C code can #include this to see the declarations. -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. -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. -debug-define Debugging option. Print #defines. -debug-gcc Debugging option. Trace C compiler execution and output.
Traduzido de golang.org