Here is an example that works on Clang.
The approach uses a builtin that is available on both Clang and GCC. I have verified Clang (see Code Explorer link below), but I have not attempted with GCC.
#include <iostream>
/* Byte offsets are numbered here without accounting for padding (will not be correct). */
struct A { uint64_t byte_0, byte_8; uint32_t byte_16; };
struct B { uint16_t byte_20, byte_24; uint8_t byte28; uint64_t byte_29; };
struct C { uint32_t byte_37; uint8_t byte_41; };
struct D { uint64_t byte_42; };
struct E : A, B, C, D {};
template<typename Type, typename Base> constexpr const uintmax_t
offsetByStaticCast() {
constexpr const Type* Type_this = __builtin_constant_p( reinterpret_cast<const Type*>(0x1) )
? reinterpret_cast<const Type*>(0x1)
: reinterpret_cast<const Type*>(0x1);
constexpr const Base* Base_this = static_cast<const Base*>( Type_this );
constexpr const uint8_t* Type_this_bytes = __builtin_constant_p( reinterpret_cast<const uint8_t*>(Type_this) )
? reinterpret_cast<const uint8_t*>(Type_this)
: reinterpret_cast<const uint8_t*>(Type_this);
constexpr const uint8_t* Base_this_bytes = __builtin_constant_p( reinterpret_cast<const uint8_t*>(Base_this) )
? reinterpret_cast<const uint8_t*>(Base_this)
: reinterpret_cast<const uint8_t*>(Base_this);
constexpr const uintmax_t Base_offset = Base_this_bytes - Type_this_bytes;
return Base_offset;
}
int main()
{
std::cout << "Size of A: " << sizeof(A) << std::endl;
std::cout << "Size of B: " << sizeof(B) << std::endl;
std::cout << "Size of C: " << sizeof(C) << std::endl;
std::cout << "Size of D: " << sizeof(D) << std::endl;
std::cout << "Size of E: " << sizeof(E) << std::endl;
/* Actual byte offsets account for padding. */
std::cout << "A offset via offsetByStaticCast<E, A>(): " << offsetByStaticCast<E, A>() << std::endl;
std::cout << "B offset via offsetByStaticCast<E, B>(): " << offsetByStaticCast<E, B>() << std::endl;
std::cout << "C offset via offsetByStaticCast<E, C>(): " << offsetByStaticCast<E, C>() << std::endl;
std::cout << "D offset via offsetByStaticCast<E, D>(): " << offsetByStaticCast<E, D>() << std::endl;
return 0;
}
Output:
Size of A: 24
Size of B: 16
Size of C: 8
Size of D: 8
Size of E: 56
A offset via offsetByStaticCast<E, A>(): 0
B offset via offsetByStaticCast<E, B>(): 24
C offset via offsetByStaticCast<E, C>(): 40
D offset via offsetByStaticCast<E, D>(): 48
Program ended with exit code: 0
Code available on Compiler Explorer: https://godbolt.org/z/Gfe6YK
Based on helpful comments from constexpr and initialization of a static const void pointer with reinterpret cast, which compiler is right?, particularly including the link to a corresponding LLVM commit http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20120130/052477.html