有一些C对象,如联合,包含位域的结构和结构与Go的ABI不同的结构,无法从Go访问。其中一些结构无法更改为可从Go代码访问,因为它们是现有库的API的一部分。
要将这些对象编组到Go结构中,我们就不能真正使用Go代码。相反,我必须在C中编写编组代码。这很好但我没有找到一种可行的方法来定义对Go代码中定义的类型进行操作的C函数。现在我正在定义我在C端编组的数据类型,并在我的Go代码中使用这些数据类型。
如果我想在我的Go代码中将编组类型公开为API,这真的很讨厌,因为我不能将C类型作为我的包接口的一部分公开。我当前的方法涉及将已编组的对象重新编组为Go代码中定义的类型。
是否有更优雅的方式来做我想做的事情,即编组无法从Go代码访问的C结构到Go代码中定义的数据类型?
根据评论部分的要求,这里是无法从Go访问的C对象的集合。
#include <complex.h>
#include <stdbool.h>
union foo {
int i;
float f;
};
struct bar {
bool x:1;
unsigned int y:3;
unsigned int z:4;
};
struct baz {
float f;
complex float c;
};
#pragma pack 1
struct quux {
char c;
short s;
int i;
};
答案 0 :(得分:1)
标准包encoding/binary
可用于操作原始C结构。
您可以扩展读取和写入功能以支持自定义类型:
func Read(r io.Reader, order binary.ByteOrder, data interface{}) error {
switch data := data.(type) {
case *foo:
return readFoo(r, order, data)
// (...)
default:
return binary.Read(r, order, data)
}
}
func Write(w io.Writer, order binary.ByteOrder, data interface{}) error {
switch data := data.(type) {
case foo:
return writeFoo(r, order, data)
// (...)
default:
return binary.Write(r, order, data)
}
}
使用包含所有联合字段的结构,并使用应用程序上下文来决定将哪个值编码到C联合中。
type foo struct {
is_i bool
i int32
f float32
}
// Read a foo from r into data
func readFoo(r io.Reader, order binary.ByteOrder, data *foo) error {
b := make([]byte, 4)
if _, err := io.ReadFull(r, b); err != nil {
return err
}
*data = foo{
i: int32(order.PutUint32(b)),
f: float32(order.PutUint32(b)),
}
return nil
}
// Write a foo from data into w
func writeFoo(w io.Writer, order binary.ByteOrder, data foo) error {
b := make([]byte, 4)
if data.is_i {
order.PutUint32(b, uint32(data.i))
} else {
order.PutUint32(b, uint32(data.f))
}
_, err := w.Write(b)
return err
}
(或者,使用getter和setter:http://pastebin.com/H1QW5AFb)
使用按位运算来编组位域
type bar struct {
x bool
y uint
z uint
}
// Read a bar from r into data
func readBar(r io.Reader, order binary.ByteOrder, data *foo) error {
b := make([]byte, 1)
if _, err := io.ReadFull(r, b); err != nil {
return err
}
// Read from bitfield
*data = bar{
x: bool(b[0] >> 7), // bool x:1;
y: uint((b[0] & 0x70) >> 3), // unsigned int y:3;
z: uint(b[0] & 0x0f), // unsigned int z:4;
}
return nil
}
// Write a bar from data into w
func writeBar(w io.Writer, order binary.ByteOrder, data bar) error {
b := make([]byte, 1)
var x uint8
if data.x {
x = 1
}
// Create bitfield
b[0] = (x & 0x01 << 7) & // bool x:1;
(uint8(data.y) & 0x03 << 4) & // unsigned int y:3;
(uint8(data.z) & 0x04) // unsigned int z:4;
_, err := w.Write(b)
return err
}
baz的序列化形式取决于编译器的复杂内部定义。
当使用encoding.binary
时,字段具有1字节的对齐,因此可以直接编组quux。