将c语言的结构体定义变成对应的golang语言的结构体定义,并将golang语言结构体变量的指针传递给c语言,cast C struct to Go struct

时间:2023-03-08 17:46:18

https://groups.google.com/forum/#!topic/golang-nuts/JkvR4dQy9t4

https://golang.org/misc/cgo/gmp/gmp.go

https://*.com/questions/19910647/pass-struct-and-array-of-structs-to-c-function-from-go

https://studygolang.com/articles/6367

1、可以为c struct定义结构体函数,如下定义的打印函数,(你可能还可以定义改变结构体内部子field的函数,但我未验证过):

working with a lot of typedefs in cgo is a real pain (Go's typing rules are simply too strict
for a C programmer).
I'd suggest you create a wrapper function in C (or Go) to create the structure for you. 
for printing, you can define the String method on real type of structure (it won't be portable,
as it depends the real type name of the C struct, but it's certainly doable, and will save you
a lot of work if you're debugging a C-Type-rich application)
For example,
package main

/*
struct CType {
int a;
char b;
float c;
};
*/
import "C" import "fmt" func (c _Ctype_struct_CType) String() string { return "hello" } func main() {
fmt.Printf("%v\n", C.struct_CType{})
}

其中的 _Ctype_struct_CType 寒气来有点奇怪,起始不用惊慌,可以用后面的方法自动得到这种奇怪的命名的。

当然,上面的只是演示为 c struct 定义内置函数。如果你仅仅是在golang中打印 c struct,只需要像普通变量一样直接传给Print就可以了,如: fmt.Printf("%v\n", C.objstruct) 或 fmt.Println(C.objstruct)。

2、你可以将整个golang的结构体指针转换成c语言的结构体指针,前提是golang 的结构体和c 的结构体定义是一一对应的(后面有介绍怎么穿件一一对应的结构体),但是c语言的结构体指针无法直接转换成golang语言的结构体指针:

You can cast the entire struct via intermediate pointers. The conversion should work in either direction. There are probably other ways too, like encoding/binary. An example of the pointer approach follows:

package main

import (
"fmt"
"unsafe"
) // struct x {
// int y, z;
// };
//
// int sum(struct x a) {
// return a.y + a.z;
// }
//
import "C" type X struct{ Y, Z int32 } func main() {
a := &X{, }
fmt.Println(a, "->", C.sum(*((*C.struct_x)(unsafe.Pointer(a)))))
}
3、上面第二步的做法并不安全,因为这种程序员自己定义的golang语言结构体可能和c语言的结构体并不一一对应,因此应该用下面的方法:
This is not safe and Go doesn't guarantee compatible struct layout rules with gcc.
for example, 8g currently aligns uint64 only to 4-byte boundary, but gcc aligns it to 8-byte boundary.
If you want compatible structure layout, you can use "cgo -godefs".
for example, given file.go:
package main

/*
#include <stdio.h>
*/
import "C" type File C.FILE
const Sizeof_File = C.sizeof_FILE
go tool cgo -godefs file.go                will generate a Go definition of type File that matches that of C's FILE.

cgo -godefs  是专门用来将c语言结构体转换成golang语言对应的结构体的工具。

4、示例:

package main

/*
#include <stdio.h> typedef struct {
int a;
int b;
} Foo; void pass_struct(Foo *in) { printf("%d : %d\n", in->a, in->b); } void pass_array(Foo **in, int len) {
for(int i = 0; i < len; i++) {
pass_struct(in[i]);
in[i]->a += 1;
in[i]->b += 1;
}
}
*/
import "C" import (
"fmt"
"unsafe"
) type Foo struct{ a, b int32 } func main() {
foo := Foo{, }
foos := []*Foo{&Foo{, }, &Foo{, }} fmt.Println("from C land")
C.pass_struct((*C.Foo)(unsafe.Pointer(&foo)))
C.pass_array((**C.Foo)(unsafe.Pointer(&foos[])), C.int(len(foos)))
fmt.Println("a & b should have incremented with 1") fmt.Println("from Go land")
for _, foo := range foos {
fmt.Printf("%d : %d\n", foo.a, foo.b)
}
}
Output:
from C land
:
:
:
a & b should have incremented with
from Go land
:
:

5、示例2:

将c语言的 char * 指针copy成 golang 的byte slice:

// convert c language char* to golang byte slice, and COPY source datas into dest slice
packet *C.char // 该变量在c语言里赋值
vlen :=
b := make([]byte, vlen)
ii := C.int()
for i := ; i < vlen; i++ {
ii = C.int(i)
// this is copy, not equal to the orignal pointer
b[i] = *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(packet)) + uintptr(unsafe.Pointer(C.int_to_charp(ii)))))
}

我自己的转换的示例:

c语言定义的结构体:

struct dnet_host {
struct dnet_key key;
char name[DNET_HOST_NAME_MAX];
char version[];
time_t last_active;
time_t last_appear;
time_t last_time_changed;
uint32_t crc32;
uint32_t route_ip;
unsigned name_len;
uint16_t route_port;
uint8_t is_local;
uint8_t is_trusted;
enum dnet_host_route_types route_type;
};

转成golang对应的结构体 (type   DH   C.struct_dnet_host):

type DH struct {
Key _Ctype_struct_dnet_key
Name []int8
Version []int8
Last_active int64
Last_appear int64
Last_time_changed int64
Crc32 uint32
Route_ip uint32
Name_len uint32
Route_port uint16
Is_local uint8
Is_trusted uint8
Route_type uint32
Pad_cgo_0 []byte
}