Original text: chenquan. Im/go / 2019/08 /…

Cgo passes slice from GO to C

Suppose you have a Go [] int and want to pass it to C * int, you can do this:

package main

/* #include
      
        void slice(int *a){ for(int i=0; i<4; i++){ printf("%d\n",a[i]); }} * /
      
import "C"
import (
	"fmt"
)

func main(a) {

	intSlice := []C.int{108880.18.28.83.488} // Use cGO type c.innt
	fmt.Println(&intSlice[0])
	C.slice(&intSlice[0])}Copy the code

We can see that when we pass the address of the first element in the Go slice to the slice(int *a) function in C, we can print out all the elements in the original Go slice. Therefore, we can know that the first element in the Go slice is actually the first element of the Array in C, and the memory is not reallocated. Here’s a case to prove it:

package main

import "C"
import (
	"fmt"
	"reflect"
	"unsafe"
)

func main(a) {
	var c int = 1

	intSlice := []int{100.1.2.3.4}
	newSlice := intSlice[c:]

	fmt.Printf("Pointer to slice %p\n", &intSlice)            //0xc000004460
	fmt.Printf("Point to the first element of the underlying array: %d\n", &intSlice[0]) / / 824633770800

	fmt.Printf("Point to the first element of newSlice instead of the array: %d\n", &newSlice[0]) / / 824633770808
	fmt.Printf("%v \n", newSlice[0])                       / / 1

	ref := reflect.ValueOf(newSlice)
	t := reflect.TypeOf(newSlice)

	// The starting address of the underlying array
	addr := int(ref.Pointer()) - (t.Align() * c) / / 824633770800

	fmt.Printf("Address of underlying data: %d\n", addr) / / 824633770800

	underArray := (*[5]int)(unsafe.Pointer(uintptr(addr)))
	fmt.Println(*underArray) //[100 1 2 3 4]

}

Copy the code

Cgo passes slice from C to GO

This is typically used in c’s callback.

You need to add a wrapper, which is one more conversion step than calling go directly, but much more convenient.

C call initiation -> C Wrapper -> Go export

.go:

//export a_function_callback
 func a_function_callback(args []C.astruct) {}
Copy the code

.c:

extern void a_function_callback(GoSlice args);

void a_function_callback_c(args *C.astruct) {
    GoSlice args_go;
    args_go.data = args;
    args_go.len = nargs;
    args_go.cap = nargs;
    return a_function_callback(args_go);
}
Copy the code

Here, the GoSlice needs to be obtained from the.h file exported, or include the.h file. You can also copy it directly, taking care not to create type conflicts.

#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H

 typedef long long GoInt64;
 typedef GoInt64 GoInt;
 typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

 #endif
Copy the code

C.CString automatic collection method

Typically, when using c.string, you define a temporary variable to store the results and use defer C.tree to reclaim the temporary variable.

In the Go Runtime package, there is a SetFinalizer method that calls a handler when an object is reclaimed.

Then we can consider using it to automate the collection of temporary variables generated by c. String.

       type CString struct {
             Ptr *C.char
       }

       func CCString(s string) *CString {
            p := C.CString(s)
             this := &CString{Ptr: p}

            runtime.SetFinalizer(this, freeCString)
             return this
       }

       func freeCString(cs *CString) {
              C.free(unsafe.Pointer(cs.Ptr))
       }
Copy the code

When used, you can basically embed it at the location of the call without explicitly defining a temporary variable and executing defer C.tree.

       C.somefunc(CCString(s).Ptr)
Copy the code

Note that this method is only called if someFunc does not take over ownership of the parameters passed, and if someFunc takes over ownership and memory is reclaimed at some point in the future, the program SEGFAULT crashes.

This may result in a small loss of runtime efficiency, plus additional memory allocation, depending on how you use it.

Bool conversion processing

In C, before C99, a bool was defined as an int, so it’s a bit tricky to convert it to a bool of go.

     var bval_c C.GBoolean
     var bval_go bool
     if int(bval) == 1  { 
           bval_go = true
     } else {
           bval_go = false
     }
Copy the code

This conversion could also be done in one line of code if GO had a ternary operator, but go does not have one.

In general, it’s a good idea to wrap this with a simple conversion function, such as C2GObool (C.GBoolean).

Sometimes bool definitions vary from C project to C project, so you usually need to define a function for each project rather than use it in a library.

After C99, we don’t have this problem, cGO can directly determine that it is a built-in bool and can directly convert it to a BOOL of GO.

     var bval_c C._Bool
     var bval_go = bool(bval_c)
Copy the code