2

How can I verify the layout of a (repr(C)) structure without running the code? E.g. when I have

#[repr(C)]
struct Registers {
    urxd:       u32,        // 0x00
    _rsrvd0:    [u32;15],
    utxd:       u32,        // 0x40
    _rsrvd1:    [u32;15],
    ucr1:       u32,        // 0x80
}

how can I make the build process fail when ucr1 is not at positition 0x80 (e.g. due to miscalculated _rsrvd members or target depending padding)?

In C I would write something like

struct foo {
    uint32_t    a;
    uint32_t    b;
    uint32_t    c;
    uint32_t    d;
};

static void _test() {
    _Static_assert(offsetof(struct foo, d) == 12);
}

For _Static_assert there seem to exist crates like static_assertions which implement hacks like these from the good old C times (negative array sizes and so).

But for offsetof() I have found only non-const implementations.

Code is for embedded platforms without #[test] support so I can not test it at runtime. Running #[test] on a std-platform might give wrong results because padding/alignment is different there.

Herohtar
  • 5,347
  • 4
  • 31
  • 41
ensc
  • 6,704
  • 14
  • 22

1 Answers1

2

You can use the const_field_offset crate to get the offset and the static_assertions to assert during the build.

use const_field_offset;
use static_assertions as sa;

#[repr(C)]
#[derive(const_field_offset::FieldOffsets)]
struct Registers {
    urxd:       u32,        // 0x00
    _rsrvd0:    [u32;15],
    utxd:       u32,        // 0x40
    _rsrvd1:    [u32;15],
    ucr1:       u32,        // 0x80
}

sa::const_assert!(0x80 == Registers::FIELD_OFFSETS.ucr1.get_byte_offset());

When the assert fails, the error message isn't super helpful, but it does at least fail the build:

sa::const_assert!(0x79 == Registers::FIELD_OFFSETS.ucr1.get_byte_offset());
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow
kmdreko
  • 42,554
  • 6
  • 57
  • 106
Andrew
  • 904
  • 5
  • 17