Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make my objects reinterpret-castable to arrays, like std::complex?

Tags:

c++

c++11

I just learned this because of this question, that the standard states for std::complex (26.4 [complex.numbers]):

4 If z is an lvalue expression of type cv std::complex<T> then:
— the expression reinterpret_cast<cv T(&)[2]>(z) shall be well-formed,
reinterpret_cast<cv T(&)[2]>(z)[0] shall designate the real part of z, and
reinterpret_cast<cv T(&)[2]>(z)[1] shall designate the imaginary part of z.
Moreover, if a is an expression of type cv std::complex<T>* and the expression a[i] is well-defined for an integer expression i, then:
reinterpret_cast<cv T*>(a)[2*i] shall designate the real part of a[i], and
reinterpret_cast<cv T*>(a)[2*i + 1] shall designate the imaginary part of a[i].

This is something I really want to take advantage of in a standards-conforming manner. There are times when I have PODs, like mathematical vectors, which are composed of a single data type. Here are two example classes:

template <typename T, unsigned N>
struct Vector
{
    T v[N];
};

template <typename T>
struct Quaternion
{
    T r, i, j, k;
};

From what I understand, the implementation is allowed to add padding after the last member, as well as between members. Which means that sizeof(Quaterntion<float>) may not equal sizeof(float[4]), and sizeof(Vector<double, 8>) may not equal sizeof(double[8]). This means I typically have to add some static_asserts to my code to make sure that I can cast my Vector<float, N>/Quaterntion<float> to a float*, for example, and not worry about padding (for passing to C libraries or OpenGL buffers, for example).

Is there some method provided by the standard that would allow me to have the same guarantees for my little PODs, like Vector and Quaternion, as std::complex does? I'm aware of implementation-specific things, like __attribute__((packed)). I'm looking for for a non-implementation specific, standards conforming way of doing this. Since the standard requires support for this type of thing for implementations that provide std::complex, I'm wondering if there's also some standard way of applying this guarantee to my own classes.

like image 659
Cornstalks Avatar asked Apr 08 '14 01:04

Cornstalks


1 Answers

I think you are asking the impossible.

Keep in mind that standard library implementors often rely on non-standard extensions or implementation-defined behavior. Indeed, in VC++'s complex header we find:

#pragma pack(push, _CRT_PACKING)

// implementation

#pragma pack(pop)

What you could do for your Quaternion is to place all the members inside an array, since the struct address can be reinterpret_cast to a pointer to the first member. But I guess that kind of defeats the purpose of the struct (direct member access by name).

It is not exactly what you ask for, but providing an

operator const T*() const // can be written in a portable manner

for your struct, will allow you to write

Quaternion<double> q = {};
const double * p = q;

at the cost of additional runtime/memory overhead, depending on how you implement the conversion operator.

like image 73
decltype Avatar answered Sep 17 '22 18:09

decltype