5

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

1 回答 1

1

标准包encoding/binary可用于操作原始 C 结构。您可以扩展 Read 和 Write 函数以支持自定义类型:

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 的序列化形式取决于编译器对 complex 的内部定义。使用时encoding.binary,字段有 1 字节对齐,因此 quux 可以直接编组。

于 2014-06-17T14:55:35.980 回答