编组无法从Go访问的C对象

时间:2014-05-28 18:54:44

标签: go marshalling cgo

有一些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;
};

1 个答案:

答案 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。