emulate offsetof() for c++ using temporary enclosing object?

Hi,
offsetof() is not much worth in c++ as it can only be used on standard-layout classes, so I wonder if the following code conforms to the standard?

It computes the enclosing class pointer from a member variable pointer by using a temporary object of the enclosing class to compute the offset.

I wonder about this because it assumes that the internal layout of the A part embedded inside B always equals the layout of a standalone object A?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Base {
  virtual ~Base() {}
private:
  double dummy1;
};

struct A : virtual public Base {
  double test;
};

struct B : public A {
  int dummy2;
};

// A and B not standard-layout.

#include <iostream>

int main()
{
  B b;
  b.test = 100;

  A* a_inside_b_simple = &b;

  A a;
  A* a_inside_b_hairy = reinterpret_cast<A*>(
                          reinterpret_cast<char*>( &b.test )
                             - size_t( &reinterpret_cast<char&>( a.test )
                                     - &reinterpret_cast<char&>( a ) ) );

  std::cout << a_inside_b_simple->test << std::endl; // prints 100
  std::cout << a_inside_b_hairy->test << std::endl; // prints 100
}

offsetof is only *meaningful* with standard-layout classes.

Your "hairy" code enters undefined behavior multiple times, from pointer arithmetic to accessing aliased lvalue of the wrong type, but for certain kinds of objects, with certain compilers, it can work.

For fun, make dummy1 public and try accessing it the same way.
Thanks for the answer!

Surely it won't work when accessing dummy1 the same way (by substituting test in the expression), but then A is not the immediate enclosing class to dummy1. Replacing both test with dummy1 and A with Base, it still works fine.

I still struggle a little how it will ever fail with the premise that you only use the immediate enclosing class when computing member offset. E.g.
1
2
3
B b;
Base base;
Base* basePtr = &b;

So you are you saying that (&basePtr->dummy1 - basePtr) may be different from (&base.dummy1 - &base), even when dummy1 is defined immediately inside class Base?

It seems rather strange to me why anyone would make such a compiler, but I suppose you are right ;)
Topic archived. No new replies allowed.