Go vs. C language Interoperability

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Go has a strong C background, in addition to the inheritance of grammar, its designers and its design goals are inextricably linked to C language. Go is a powerful support for go and C language Interoperability (interoperability). Especially if you use C in go, you can even write C code directly in the go source file, which is beyond the reach of other languages.

In some of the following scenarios, you may be involved in the interop of Go and C: 1, when you promote local code performance, replace some go code with C. C is to go, like a compilation of C. 2, too Go memory GC performance is not enough, you manually manage the application memory. 3, the implementation of some library go Wrapper. For example, the C version of OCI provided by Oracle, but Oracle does not provide the go version and the protocol details of the connection db, so it can only be packaged in C OCI version to provide go developer use. 4, go export function for C developers to use (currently this need should be very rare). 5, maybe more ... first, go calls the principle of C codeHere is a short example: package main//#include <stdio.h>//#include <stdlib.h>/*void print (char *str) {printf ("%s\n", STR);} */import "C" import "unsafe" func main () {s: = "Hello Cgo" cs: = c.cstring (s) c.print (CS) c.free (unsafe. Pointer (CS)}} compared to the "normal" go code, the above code has several "special" places: 1) in the beginning of the comments appear in the C header file of the include Word 2) in the comments defined in the C function print3) import a "package" named C 4) In the main function actually called the above-mentioned C function-print Yes, this is the go source code in the process of calling C, you can see that we can directly in the Go source code file to write C code. First, the C code in the go source file needs to be wrapped in comments, like the Include header file above and the print function definition, and then the import "C" statement is required, and it cannot be separated from the above C code by a blank line, and must be closely connected. Here the "C" is not a package name, but a similar namespace concept, or can be understood as pseudo-package, C language all the syntax elements are under the pseudo-package, and finally, access to the C syntax element is preceded by a pseudo-package prefix, such as C.uint and the above code, such as C.print, C.free, and so on. How do we compile this go source file? In fact, the "normal" go source file is no different, can still be directly through go build or go run to compile and execute. However, during the actual compilation process, go calls a tool called CGO, CGO will recognize and read the C element in the go source file, and extract it to the C compiler to compile, and finally with the go source compiled target file link into an executable program. This makes it easy to understand why the C code in the go source file is wrapped in annotations, and these special grammars can be identified and used by CGO. second, the type of C language used in Go 1, native type  * numeric types can access C native numeric types in go as follows:  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), the numeric type of c.float,c.double go corresponds to a numeric type in C that is not one by one. So there is an explicit transition operation when using the other type variable, like this example in Go doc:  func Random () int {    return int (C.random ())//c.long-Go int}  func Seed (i int) {    C.srandom (C.uint (i))//go for uint} * pointer type C, pointer type of the native numeric type can be preceded by the type by the GO syntax , such as Var P *c.int. And void* is more special, with go in the unsafe.pointer expression. Any type of pointer value can be converted to unsafe. Pointer type, while unsafe. The pointer type value can also be converted to a pointer value of any type. Unsafe. Pointer can also be converted to and from the UIntPtr type. Due to unsafe. The pointer pointer type cannot do arithmetic operations, and after conversion to uintptr, arithmetic operations can be performed.  * there is no formal string type in the C language of the string type, a character array with the trailing ' s ' in C to represent the string, and in Go, the string type is the native type, so in the case of two languages interoperability is bound to do the conversion of the string type.   Through the c.cstring function, we can convert the string type of go to the "string" type of C, and then pass it to the C function. As we used in the opening example of this article:  s: = "Hello cgo\n" cs: = c.cstring (s) c.print (CS)   However, the resulting C string CS cannot be managed by the go GC, we must handThe memory occupied by the CS is released, which is why the last call to C.free released CS in the example. In the memory allocated within C, the GC in Go is not perceptible, so remember to release it.   Converts the C string (*c.char) to the string type of go by c.gostring, for example:  //#include <stdio.h>//#include <stdlib.h>/ /char *foo = "Hellofoo"; import "C"  import "FMT"  func main () {... ...    FMT. Printf ("%s\n", C.gostring (C.foo))} * array types in the C language are significantly different from the array in the Go language, which is a value type, whereas the former and the pointer in C are freely convertible in most cases. There seems to be no direct transformation between the two in the present, nor is official documentation. But we can do this by writing a conversion function, converting the array of C to go slice (because the array in Go is a value type, its size is static, the conversion to slice is more general), here is an example of an integer array conversion:  //int carray[] = {1, 2, 3, 4 , 5, 6, 7}; func Carraytogoarray (CArray unsafe. Pointer, size int) (Goarray []int) {    P: = UIntPtr (cArray)     for I: =0; i < size; i++ {  &N Bsp     J: = * (*int) (unsafe. Pointer (p))         Goarray = append (Goarray, j)         p + = unsafe. Sizeof (j)    }     Return} func main () {    ... ...    Goarray: = Carraytogo Array (unsafe. Pointer (&c.carray[0]), 7)     FMT. Println (goarray)}  execution Output: [1 2 3 4 5 6 7]  Note here: The go compiler does not automatically convert the CArray of C to the address of an array, so you cannot pass the variable directly to the function as if you were using an array in C. Instead, the address of the first element of the array is passed to the function. &NBSP;2, custom types   In addition to native types, we can also access custom types in C.  * enum (enum)  //enum color {//   red,//   blue,//   yellow//}; var E, F, g C.enum_color = c.red, C.blue, C.yellowfmt.println (E, F, g)   output: 0 1 2  for a named C enumeration type, we can access that type through C.ENUM_XX. If it is an anonymous enumeration, it appears that only its fields are accessible.  * struct (struct)  //struct Employee {//    char *id;//    int  age;//}; id: = c.c String ("1247") var employee c.struct_employee = C.struct_employee{id, 21}fmt. Println (c.gostring (employee.id)) fmt. Println (employee.age) c.free (unsafe. Pointer (ID))   output:124721  similar to enum, we can access the struct type defined in C by c.struct_xx.  * Union (Union)   Here I'm trying to access a C's union: //using the same method as the access struct #include <stdio.h>//Union bar {//       char   c;//       int    i;//       double d;//};import "C"  func Main () {    var b *c.union_bar = new (C.union_bar)   & nbsp B.C = 4    FMT. Println (b)}  but at compile time, go has an error: B.C undefined (type *[8]byte has no field or method C). Judging from the error message, go treats union differently from other types and seems to treat Union as [N]byte], where N is the size of the largest field in the Union (rounded), so we can handle c.union_bar: func as follows Main () {    var b *c.union_bar = new (C.union_bar)     b[0] = 13    B[1] = 17    FMT . Println (b)}  output: &[13 0 0 0 0 0 0] * typedef accessing the alias type defined by typedef in GO is accessed in the same way as the original actual type. such as:  //typedef int MYINT;&NBSP;VAR a c.myint = 5fmt. Println (a)  //typedef struct employee Myemployee; var m c.struct_myemployee  as you can see from the example, the alias of the native type, Access this new type name directly. For the alias of a composite type, the new alias needs to be accessed based on the access of the original composite type, such as the Myemployee actual type is a struct, then the use of Myemployee is also prefixed with struct_.   third, go access to C's variables and functions   In fact, in the example above we have shown how to access C's variables and functions in go, the general method is to add a C prefix, especially for functions in the C standard library. However, although we can directly define C variables and C functions in the Go source file, in terms of code structure, a lot of writing C code in Go source does not seem so "professional". How do you separate the C function and the variable definition from Go source and define it separately? It's easy to imagine that C's code is provided to go source in the form of a shared library. &NBSP;CGO provides the #cgo indicator to specify which shared libraries the go source will link to after compilation. Let's take a look at the example:  package main //#cgo ldflags:-L./-lfoo//#include <stdio.h>//#include <stdlib.h>//#i Nclude "foo.h" import "C" import "FMT"  func Main () {    FMT. Println (c.count)     C.foo ()}  We see in the example above with the #cgo designator that tells the go compiler to link the Libfoo shared library under the current directory. Both the C.count variable and the C.foo function are defined in the Libfoo shared library. Let's create this shared library:  //foo.h int count;void foo ()  //foo.c#include "Foo.h"  int count = 6;void foo () {    printf ("I am foo!\n");}  $> gcc-c foo.c$> ar rv libfoo.a foo.o  We first created a static shared library libfoo.a, but we were having problems compiling the go source file: $> Go build foo.go# COMMAND-LINE-ARGUMENTS/TMP/GO-BUILD565913544/COMMAND-LINE-ARGUMENTS.A (Foo.cgo2.) (. Text): Foo:not definedfoo (0): not defined  hint foo function is undefined. The specific compilation details are printed with the-X option, and the problem is not identified. But in the go Question list I sentNow a issue (http://code.google.com/p/go/issues/detail?id=3755), mentioned above the current version of Go does not support the link static shared library.   Let's create a dynamic shared library try: $> gcc-c foo.c$> gcc-shared-wl,-soname,libfoo.so-o libfoo.so  foo.o  It is true that the Foo.go can be successfully compiled. Executes Foo.  $> Go build foo.go && go6i am foo!  It's also worth noting that go supports multiple return values, and C doesn't support it. So when the C function is used in calls with multiple return values, the errno of C is returned as the Err return value, below is an example:  package main //#include <stdlib.h>//#include < stdio.h>//#include <errno.h>//int foo (int i) {//   errno = 0;//   if (i > 5) {//&NBSP ;      errno = 8;//       return i–5;//   } else {//      & Nbsp;return i;//   }//}import "C" import "FMT"  func Main () {    I, Err: = C.foo (C.int (8))   & nbsp If err! = Nil {        FMT. PRINTLN (Err)    } else {        FMT. Println (i)    }} $> go run foo.goexec format Error errno is 8, meaning ErrnO.h can be found in:  #define ENOEXEC      8  /* Exec format error */  is indeed "Exec format error".   use Go function in CThere are fewer occasions to use the GO function in C compared to using C source in go. In go, you can export the Go function to C using the "Export + function name" To see a simple example: package main/* #include <stdio.h> extern void Goexportedfunc ();        void Bar () {printf ("I am bar!\n"); Goexportedfunc ();} */import "C" import "FMT"//export goexportedfuncfunc Goexportedfunc () {fmt. Println ("I am a goexportedfunc!")} Func Main () {C.bar ()} But when we compile the go file, we get the following error message: # Command-line-arguments/tmp/go-build163255970/command-line-argum ents/_obj/bar.cgo2.o:in function ' Bar ':./bar.go:7: Multiple definition of ' bar '/tmp/go-build163255970/ Command-line-arguments/_obj/_cgo_export.o:/home/tonybai/test/go/bar.go:7: First defined herecollect2:ld returned 1 The exit status code does not seem to have any problems, but it is not possible to compile and always prompt for "multiple definitions." Look at the CGO documents and find some clues. Originally There is a Limitation:if your program uses any//export directives and then the C code in the comment if only include D Eclarations (extern int f ();), not definitions (int f () {return 1;}).Seems to be//extern int F () and//export F cannot be placed in a go source file. We split the bar.go into Bar1.go and bar2.go two files://Bar1.go Package main/* #include <stdio.h> extern void Goexportedfunc ();        void Bar () {printf ("I am bar!\n"); Goexportedfunc ();} */import "C" func main () {C.bar ()}//Bar2.go package main import "C" import "FMT"//export goexportedfuncfunc Goex Portedfunc () {fmt. Println ("I am a goexportedfunc!")} Compile execution: $> go build-o bar bar1.go bar2.go$> BarI am bar! I am a goexportedfunc! Personally feel that go for the export function for C use of the function is very limited, the two languages of the calling convention is different, the type can not be mapped and the go in the same way as GC advanced features such as the export go function is difficult to achieve a perfect function, the exported function is still not completely out of the go environment, so practicality seems to be discounted. v. OtherWhile go provides powerful interoperability with C, it is still not perfect, such as a function (issue975) that calls a variable number of parameters directly in go, such as printf (and therefore, multiple fputs in a document). Here's the advice: try to narrow the range of interoperability between go and C. What do you mean? If you are using C code in go, try calling C functions in C code as much as possible. Go only use one of the C functions that you have packaged best. Do not like the following code: C.fputs (...) C.atoi (..) C.malloc (..) instead encapsulates these C function calls into a C function, and go only knows the C function. C.foo (..) Instead, the same is true for functions exported using go in C.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.