I have some code that implements a kind of run-time reflection. In order to get a pointer to a field of a class inside a given instance, i basically take the pointer to the class instance and add a fixed offset that is calculated once for each field that is exposed to the reflection library.
I kept the implementation quite simple, since i didn’t need to support multiple inheritance and i made the mistake of not taking into account that, even with single inheritance, this situation is possible:
class A
{
public:
unsigned int m_uiField;
};
class B : public A
{
virtual void VirtualMethod()
{
}
};
int main()
{
unsigned int uiOffsetA(reinterpret_cast<unsigned int>(&(reinterpret_cast<A *>(0)->m_uiField)));
// uiOffsetA is 0 on VC9
unsigned int uiOffsetB(reinterpret_cast<unsigned int>(&(reinterpret_cast<B *>(0)->m_uiField)));
// uiOffsetB is 4 on VC9
}
In this case the virtual table pointer that my compiler puts at the beginning of each instance of B was offsetting by 4 bytes the fields of A.
My first idea was to do something similar to what i’m doing for the field offsets and store a single unsigned int as an offset for the base class to add to pointers to derived class instances together with the field offset. So, at initialization time i call this function one for each Derived class inheriting from a Base class:
template <typename Base, typename Derived>
unsigned int GetBaseClassOffset()
{
Derived *pDerived(reinterpret_cast<Derived *>(4));
Base *pBase(pDerived);
assert(pBase >= pDerived);
return reinterpret_cast<unsigned int>(pBase) - reinterpret_cast<unsigned int>(pDerived);
}
And everything seems to work with my tests using VC9.
But then it came to my mind that this area of C++ could be implementation dependent, and that other things like alignment could break this up.
In the end my question is:
Can i assume that fields of a base class will always be positioned at a constant positive offset relative to a pointer to a derived class instance?
Note: i am not saying “constant across all compilers”, i will use some code (eventually compiler dependent) to detect this offset at startup.
For this situation, you can use pointer-to-members:
See it live: http://ideone.com/U4w7j
I suggest you also look at template metaprogramming features (part of TR1 and C++11 now:), notably the
is_podtype trait:This is important because using offsetof on anything else is hazardous.