C++/Go Packed Structures Part 1

Now, this is a mess. Imagine you have this kind of structure in C++:

struct __attribute__((packed, aligned(1)))) packed
{
    char a;
    char *b;
    int c;
};

This tells the compiler that this struct should be tightly packed on 1-byte boundaries. An instance of this struct will be 13 bytes in size [1 + 8 + 4]. The problem with this structure are manifold: 1) Go can’t read from or write to the member b and c. It can read and write from the first member, a, just fine, but it fails beyond that. 2) Copying and pasting around such an object is wasteful at is it could be dozens or hundreds of bytes. 3) The member b is actually a pointer to, hopefully, a null-terminated C-string.

Problem 2 is the most straightforward to solve: a programmer passes around a pointer to a packed object and not a packed object itself. Such a pointer is 8 bytes (64 bits) wide. Most architecture can handle that in a minimal amount of executions.

Problem 1 requires the most care to solve. There are a few ways to solve this. Unfortunately, we cannot extend external types, so it’s currently not possible to write methods on a C.struct_packed type. We can write getter and setter functions which take a C.struct_packed type and convert its values to Go values, but that’s not very object-oriented.

We’ll start by creating an analog structure to packed called unpacked with similar members and one extra member called ptr which will be as wide as a pointer to a packed structure.

type Unpacked struct {
        a byte
        b string
        c int32
        ptr *C.struct_packed
}

The idea is that there are two functions in a C++ library: one generates a packed instance on the heap and returns a pointer to it, and one which consumes a pointer to a packed structure and does something with it. In this example, it will be the programmer’s job to manage memory.

What I imagine is that the unpacked struct in a Go program calls a C++ generator function as part of its constructor and obtains a pointer to a packed struct somewhere in memory. It will then read this struct and convert the information to something safely read and modified in Go using its native types and members. The user can then safely and quickly use and modify these values in Go. When it’s time to call the consumer function, the struct will update the data in memory pointed to by ptr then pass the value of ptr into the consuming C++ function.

Problem 3 will have to be carefully dealt with since it’s a pointer that’s part of a data structure which is pointed to, but that can be handled once Problem 1 is resolved.