编组无法从Go访问的C对象 [英] Marshalling C objects that cannot be accessed from Go

查看:161
本文介绍了编组无法从Go访问的C对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一些像对象一样的C对象,包含位域和结构与Go的ABI不一致的结构,这些对象不能从Go访问。由于它们是现有库的API的一部分,因此其中一些结构无法更改为可从Go代码访问。



为了将这些对象编入Go结构,我们不能真的使用Go代码。相反,必须用C编写编组代码。这工作正常,但我还没有找到一种可行的方法来定义C函数,这些函数可以对Go代码中定义的类型进行操作。现在我正在定义我在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;
复合浮点数c;
};

#pragma pack 1
struct quux {
char c;
short s;
int i;
};


解决方案

标准软件包编码/二进制可用于操作原始C结构。
您可以扩展读写函数来支持自定义类型:

$ $ $ $ $ $ $ $ $ func Read(r io.Reader,order binary .ByteOrder,data interface {})错误{
开关数据:= data。(type){$ b $ 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 union中。 b

  type foo struct {
is_i bool
i int32
f float32
}

//从r读取foo到数据
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)),
}

返回nil
}

//从数据写入foo到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



使用按位操作来编组位域

 类型栏struct {
x bool $ b $由uint
z uint
}

//从r读取数据到数据
func readBar(r io.Reader,order binary.ByteOrder,dat a * foo)错误{
b:= make([] byte,1)
if _,err:= io.ReadFull(r,b); err!= nil {
return err
}

//从位域读取
* 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;
}

返回nil
}

//将数据写入w
func writeBar(w io.Writer,order binary.ByteOrder,data bar)error {
b:= make([] byte,1)

var x uint8
if data.x {
x = 1
}
//创建位域$​​ b $ bb [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进行编组。


There are some C objects like unions, structs that contain bitfields and structs whose alignment differs from Go's ABI, that cannot be accessed from Go. Some of these structures cannot be changed to be accessible from Go code as they are part of the API of an existing library.

To marshall such objects into Go structures we thus cannot really use Go code. Instead w have to write the marshalling code in C. This works fine but I have not found a feasible way to define C functions that operate on types defined in Go code. Right now I am defining the data types I am marshalling into on the C side and use these data types in my Go code.

This is really nasty if I want to expose the marshalled types as an API in my Go code, as I cannot expose a C type as a part of my package interface. My current approach involves remarshalling the already marshalled object into a type that is defined in Go code.

Is there a more elegant way to do what I want to do, i.e. marshalling C structs that cannot be accessed from Go code into data types defined in Go code?

As requested in the comment section, here is a collection of C objects that cannot be accessed from Go.

#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;
};

解决方案

The standard package encoding/binary can be used for manipulating raw C structs. You can extend Read and Write functions to support custom types :

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)
    }
}

Use a struct containing all the union's fields and use application context to decide which value to encode into the C union.

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
}

(Alternatively, using getters and setters: http://pastebin.com/H1QW5AFb)

Use bitwise operations to marshal bitfields

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
}

The serialized form of baz depends on the compiler's internal definition of complex. When using encoding.binary, fields have a 1-byte alignement so quux can be marshaled directly.

这篇关于编组无法从Go访问的C对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆