5

Take this example of a C# struct:

    [StructLayout(LayoutKind.Explicit)]
    public struct Example
    {
        [FieldOffset(0x10)]
        public IntPtr examplePtr;

        [FieldOffset(0x18)]
        public IntPtr examplePtr2;

        [FieldOffset(0x54)]
        public int exampleInt;
    }

I can take an array of bytes, and transform it to this struct like so:

    public static T GetStructure<T>(byte[] bytes)
    {
        var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
        var structure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        handle.Free();
        return structure;
    }

    public static T GetStructure<T>(byte[] bytes, int index)
    {
        var size = Marshal.SizeOf(typeof(T));
        var tmp = new byte[size];
        Array.Copy(bytes, index, tmp, 0, size);
        return GetStructure<T>(tmp);
    }

    GetStructure<Example>(arrayOfBytes);

Is there equivalent functionality in C++ to take an array of bytes and transform it to a struct, where not all bytes are used in the transformation (C# structlayout.explicit w/ field offsets)?

I don't want to do something like the following:

struct {
  pad_bytes[0x10];
  DWORD64 = examplePtr;
  DWORD64 = examplePtr2;
  pad_bytes2[0x44];
  int exampleInt;
}
Ben
  • 749
  • 1
  • 7
  • 18
  • 1
    The last snippet exposes the way it might work or it might not. The compiler might adjust the alignment of members to its own preferences. If you want a reliable way, you may use an array of `char` (or `unsigned char`) and `memcpy()` the values at the appropriate offsets. Thereby, endianess might be another issue to care about. Beside of endianess, this will, of course, work for primitive types only. Maybe, provide a bit more context to prevent talking about an [XY problem](https://en.wikipedia.org/wiki/XY_problem). – Scheff's Cat May 16 '20 at 07:24
  • Is there a particular reason adding padding members won't work for you, or is it just the ugliness it creates? Keep in mind that the padding solution should have the equivalent of `[[gnu::packed]]`. – chris May 16 '20 at 07:25
  • FYI: [My attempt to write an MS Bitmap in a portable way](https://stackoverflow.com/a/56054487/7478597) puzzling it byte for byte together according to the found spec. It's done in C but it wouldn't look much different in C++ (except that I would prefer `fstream` instead of Cs `FILE` API). – Scheff's Cat May 16 '20 at 07:33
  • @Scheff I'm aware of how it likely works internally, but to explain and also answer chris, it's the general ugliness of having to put pads within each struct. – Ben May 19 '20 at 07:13
  • It's the clean way to do it though, if you want to stick go the typed approach. Otherwise you may just write simple marshalling code for packing/unpacking bin-data into a struct of unspecified data layout. – Ext3h May 19 '20 at 11:29
  • see https://stackoverflow.com/questions/38942125/how-to-set-structure-element-at-desired-offset – Simon Mourier May 19 '20 at 12:32
  • 1
    what about `#define FIELD_OFFSET(X) char _offset_padding_##__COUNTER__ [X]` ? – Mestkon May 20 '20 at 11:53
  • bitfields, POD & alignas? – The Floating Brain May 26 '20 at 01:21

1 Answers1

0

No, I am not aware of a way to specify the byte offset of some struct member - there is definitely nothing in the standard, and I don't know of any compiler specific extension.

In addition to padding members (as you already mentioned), you can also use alignas, #pragma pack, and __declspec(align(#)) (on MSVC), and __attribute__ ((packed)) and __attribute__ ((aligned(#))) (on GCC). Of course these don't let you specify the offset, but they can help control the layout of your struct.

The best I can think of to ensure that your layout matches your expectations is using static_assert with offsetof:

struct Example{
  char pad_bytes[0x10];
  DWORD64 examplePtr;
  DWORD64 examplePtr2;
  char pad_bytes2[0x44];
  int exampleInt;
};
static_assert(offsetof(Example, examplePtr) == 0x10);
static_assert(offsetof(Example, examplePtr2) == 0x18);
static_assert(offsetof(Example, exampleInt) == 0x54);
mpoeter
  • 2,574
  • 1
  • 5
  • 12