5

Given an aggregate struct/class in which every member variable is of the same data type:

struct MatrixStack {
    Matrix4x4 translation { ... };
    Matrix4x4 rotation { ... };
    Matrix4x4 projection { ... };
} matrixStack;

How valid is it to cast it to an array of its members? e.g.

const Matrix4x4 *ptr = reinterpret_cast<const Matrix4x4*>(&matrixStack);
assert(ptr == &matrixStack.translation);
assert(ptr + 1 == &matrixStack.rotation);
assert(ptr + 2 == &matrixStack.projection);
auto squashed = std::accumulate(ptr, ptr + 3, identity(), multiply());

I am doing this because in most cases I need named member access for clarity, whereas in some other cases I need to pass the an array into some other API. By using reinterpret_cast, I can avoid allocation.

Maarten van Stam
  • 1,901
  • 1
  • 11
  • 16
Ben
  • 282
  • 2
  • 10
  • My understanding is that using the result of a cast that won't compile except if using reinterpret_cast is almost always risking undefined behavior. This particular case will likely work as intended (unless your compiler messes up struct alignment so that the Matrix4x4 members aren't fully contiguous in memory), but it's still not guaranteed by the standard. – antred Sep 12 '16 at 09:30
  • It may work, may not, depends on Matrix4x4 actual size and data alignment settings – Anton Malyshev Sep 12 '16 at 09:35
  • Structs can have padding while [arrays never have padding](https://stackoverflow.com/questions/1066681/can-c-arrays-contain-padding-in-between-elements). – sashoalm Sep 12 '16 at 15:11
  • @antred, provided that Matrix4x4 is aligned with int (and I do have control over this), and the struct is strictly non virtual, what it's still left as a good reason for a compiler to pad before the first member and between its members? This is not a rhetorical question, I just want to know there corner cases. – Ben Sep 12 '16 at 22:29

1 Answers1

3

The cast it not required to work by the standard.

However, you can make your code safe by using static asserts that will prevent it from compiling if the assumptions are violated:

static_assert(sizeof(MatrixStack) == sizeof(Matrix4x4[3]), "Size mismatch.");
static_assert(alignof(MatrixStack) == alignof(Matrix4x4[3]), "Alignment mismatch.");
// ...
const Matrix4x4* ptr = &matrixStack.translation;
// or
auto &array = reinterpret_cast<const Matrix4x4(&)[3]>(matrixStack.translation);
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 1
    Don't you mean `==` in between the two `sizeof`/`alignof` arguments? – Aaron McDaid Sep 12 '16 at 15:05
  • And maybe add a check that `offsetof(MatrixStack, translation)==0`. Is the first element always guaranteed to have offset zero? We could check that the other two members have the expected offsets. – Aaron McDaid Sep 12 '16 at 15:18
  • 1
    Good call using static assert for verification. Thanks Maxim – Ben Sep 12 '16 at 22:30