How to Calculate Offset of a Class Member at Compile Time

Statically get offset of member variable

First of, as requested, you're goal is not achievable as the type of a impacts the offst of a inside B:

struct B
{
int some_variables[256];
A</* offset of a inside B */> a;
};

This is alignment.


You could use the standard macro offsetof. This implies two things:

  1. Since offsetof(type, member) is well-defined only for standard-layout types, the enclosing type must be standard-layout,
  2. and since offsetof can only be "called" on complete types, its statically-computed result can only be set to the subobject dynamically; it canoot be a template non-type parameter, but can be a constructor argument.

Full program

#include <cassert>
#include <cstdint>
#include <cstddef>

struct Location
{
Location(std::size_t offset) : offset_(offset) {}
std::size_t offset_;
operator std::intptr_t () const { return reinterpret_cast<std::intptr_t>(this) - offset_; }
};

struct SomeType
{
int some_variables[256];
Location location = offsetof(SomeType, location);
};

int main()
{
SomeType obj;
assert(reinterpret_cast<std::intptr_t>(&obj) == obj.location); // does pass
}

live demo

But as you commented, this is quite useless as Location could be simply defined as

template<class T>
struct Location
{
Location(T* location) : location_(location) {}
T* location_;
operator T* () const { return location; }
};

and initialized with Location location = this;.

Determining struct member byte-offsets at compile-time?

offsetof is a compile time constant, if we look at the draft C++ standard section C.3 C standard library paragraph 2 says:

The C++ standard library provides 57 standard macros from the C library, as shown in Table 149.

and the table includes offsetof. If we go to the C99 draft standard section 7.17 Common definitions paragraph 3 includes:

offsetof(type, member-designator)

which expands to an integer constant expression that has type size_t, the value of
which is the offset in bytes [...]

C++ class/structure data member offset as constant expression

Though I can't get what your compiler is,
the following code can be compiled by VC8, ideone(gcc-4.3.4), and Comeau
online:

struct A { int i; };
template< size_t > struct S;

int main() {
S< offsetof( A, i ) > *p;
}

Gcc has __offsetof__ extension.
VC seems to have a capability to take a non-compile-time constant for a template
argument strangely.
As for Comeau, I have no idea about the internal of Comeau's offsetof
unfortunately.

Incidentally, though this won't answer your question directly, as for SFINAE
purpose, since a member pointer constant can be used as a template argument
and you can specialize on it, you can write as the following:

struct A {
int i, j;
};

template< int A::* > struct S;
template<> struct S< &A::i > { static char const value = 'i'; };
template<> struct S< &A::j > { static char const value = 'j'; };

int main() {
cout<< S< &A::i >::value <<endl;
cout<< S< &A::j >::value <<endl;
}

Hope this helps.

How to output the offset of a member in a struct at compile time (C/C++)

You can use the offsetof macro, along with the C++11 static_assert feature, such as follows:

struct A {
int i;
double db;
...
unsigned test;
};

void TestOffset() {
static_assert( offsetof( A, test ) == KNOWN_VALUE, "The offset of the \"test\" variable must be KNOWN_VALUE" );
}

Any way to do compile-time check if a member is the last class data member?

You can't really assert that a member is the last of its class today. The proposals in response to N3814 might make this possible, once accepted and implemented, but they're not available today. With what is currently available, you're still sunk because of padding issues (see Csq's example in the comments).

If you ignore that the limitations of padding, and limit yourself to "plain old data" cases without virtual inheritance, you can probably use the offsetof macro, or suggestions from How to calculate offset of a class member at compile time? to perform the check you desire.

But in practice, per your comment about all your classes being protocol classes, can you not just do an assert against the known class size? It wouldn't catch cases when members were reordered, or when an addition fit within padding. But static asserts are really only there to prevent accidental incorrect changes, and you claim not to use padding. So go simple:

static_assert(sizeof(Foo) == 12, "Size of Foo has changed");

Compile time subclass byte-offset with virtual inheritance

Is it possible to compute, at compile time, the byte offset of a virtual base in an inheritance hierarchy?

No, because it's different for different object instances. It is not a property of the class.

Let's look at your example. B has a virtual base class A. So B has to have some byte offset to an A. The same is true for C.

However, D inherits a virtual A through both B and C. The whole point of virtual inheritance is that, if you inherit the same base class, then all of the paths to reach that base class will refer to the same subobject of the dynamic type. So D only has a single instance of A.

But where is it? The offsets that B and C use cannot both be correct. Unless... the offset itself is stored in the vtable.

Which is (basically) how virtual inheritance works. When you create a D, it is the D which is responsible for assigning where all of the virtually inherited objects go. Those offsets will be different from when you create an object whose dynamic type is B or C.

There's no way to do what you're trying to do. Not without actually creating instances of objects. While C++20 does allow you to create instances of virtual types at compile time (so long as they have viable constexpr constructors, so you can't do it with everything), you still can't do what you need. If you have pointers to two different types, to compute a byte offset between them, you have to reinterpret_cast those pointers into integers or unsigned char*s. And reinterpret_cast is forbidden at compile-time (and bit_cast isn't constexpr when given a pointer type or a type that contains a pointer).



Related Topics



Leave a reply



Submit