Go vs. C language Interoperability

Source: Internet
Author: User
Tags function definition

Transferred from: http://tonybai.com/2012/09/26/interoperability-between-go-and-c/

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 of 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 Go1. The native type * Numeric type can access the C native numeric type 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 long), c.float,c.double
The value type of Go is not one by one corresponding to the numeric type in C. Therefore, there is an explicit transition operation when using the other type variable, such as the 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 UINT-c UINT}
* Pointer type the type of the primitive value type can be preceded by the go syntax type *, 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. * String Type C does not have a formal string type in C, a character array with an end of ' s ' is used to represent the string, whereas in Go, the string type is a primitive type, so in the case of two languages interop is bound to do the conversion of the string type. With 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, and we have to manually release the memory occupied by CS, 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. The C string (*c.char) can be converted 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 () {... C1/>fmt. Printf ("%s\n", C.gostring (C.foo))}

* Arrays in the C language of the array differ greatly from the array in the Go language, which is a value type, while 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's slice (since 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++ {        J: = * (*int) (unsafe. Pointer (p))        Goarray = Append (Goarray, j)        p + = unsafe. Sizeof (j)    }     return} func main () {    ...    Goarray: = Carraytogoarray (unsafe. Pointer (&c.carray[0]), 7)    FMT. Println (Goarray)}
Execution output: [1 2 3 4 5 6 7] It is important to note that the go compiler does not automatically convert C's CArray to the address of an array, so it is not possible to pass arrays of variables directly to a function as in C, but rather to pass the address of the first element of the array to the function. 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 the 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.cstring ("1247") var employee c.struct_employee = C.str Uct_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 through c.struct_xx. * Union (Union) Here I try to access a C union by 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)    B.C = 4    fmt. Println (b)}

But at compile time, go 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 as follows:
Func Main () {    var b *c.union_bar = new (C.union_bar)    b[0] =    b[1] = +    FMT. Println (b)}
Output: &[13 0 0 0 0 0 0] * typedef access in Go using the alias type defined by typedef is accessed in the same way as the original actual type. Such as:
typedef int Myint; var a c.myint = 5fmt. Println (a)//typedef struct employee Myemployee; var m c.struct_myemployee
As can be seen from the example, the alias of the native type is directly accessible to the new type name. 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 variables and functionsIn fact, in the example above we have demonstrated 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. 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>//#include "foo.h" Import "C" import "FMT" Func Main () {    fmt. Println (C.count)    C.foo ()}
We see the above example with the #cgo indicator telling 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 ran into a problem 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 found a issue (http://code.google.com/p/go/issues/detail?id=3755), which mentioned that the current version of Go does not support the link static shared library. Then let's create a dynamic shared library try: $> gcc-c foo.c$> gcc-shared-wl,-soname,libfoo.so-o libfoo.so foo.o and then compiles foo.go, it does succeed. Executes Foo. $> Go build foo.go && go6i am foo! It is also worth noting that go supports multiple return values, and C does not support them. So when the C function is used in calls with multiple return values, the errno of C is returned as the Err return value, and here is an example:
Package main//#include <stdlib.h>//#include <stdio.h>//#include <errno.h>//int foo (int i) {//    E Rrno = 0;//    if (i > 5) {//        errno = 8;//        return i–5;//    } else {//        return i;//    }//}import "C" im Port "FMT" Func Main () {    I, err: = C.foo (C.int (8))    if err! = Nil {        FMT. PRINTLN (ERR)    } else {        fmt. Println (i)    }}
$> Go run foo.goexec format error errno is 8, meaning it can be found in errno.h: #define ENOEXEC 8/* EXEC Format error */is indeed "exec fo Rmat 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-ARGUMENTS/_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 exit status code doesn't seem to have any problems, but it just can't be compiled, 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 Goexportedfunc () {        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.

Go vs. C language Interoperability

Related Article

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.