views:

261

answers:

4

Is there a better method to establish the positional offset of an object's data member than the following?

class object
{
  int a;
  char b;
  int c;
};

object * o = new object();
int offset = (unsigned char *)&(object->c) - (unsigned char *)o;
delete o;
+2  A: 

In this case, your class is POD, so you can use the offsetof macro from <cstddef>.

In practice, in most implementations, for most classes, you can use the same trick which offsetof typically uses:

int offset = &(((object *)0)->c) - (object *)0;

No need to actually create an object, although you may have to fight off some compiler warnings because this is not guaranteed to work.

Beware also that if your class has any multiple inheritance, then for (at least) all but one base, (void*)(base*)some_object != (void*)(derived*)some_object. So you have to be careful what you apply the offset to. As long as you calculate and apply it relative to a pointer to the class that actually defines the field (that is, don't try to work out the offset of a base class field from a derived class pointer) you'll almost certainly be fine. There are no guarantees about object layout, but most implementations do it the obvious way.

Technically for any non-POD class, it does not make sense to talk about "the offset" of a field from the base pointer. The difference between the pointers is not required to be the same for all objects of that class.

Steve Jessop
Interesting. Is that true for classes with multiple access modifiers too?
Nicholas
Classes with multiple access modifiers are not POD. So in theory, no, neither offsetof nor the null pointer trick are valid. In practice, I don't know of any reason for compilers to play any strange tricks with layout just because of access modifiers. Even if they group all private fields and all public fields together (regardless of their order in the class definition), they would still have consistent offsets.
Steve Jessop
+2  A: 

There's no need to actually create an object:

(size_t)&(((object*)0)->c)

That is, the address of a member in an object at address zero is the offset of that member into the object.

Of course, you will need access to the member, so you need to either make it a struct or add a public: access modifier.

This is how offsetof is implemented, at least for most compilers.

Tim Sylvester
+2  A: 

Rather than a pointer.
You can use a pointer to a member.

class X;
typedef int X::*    PtrToMemberInt;  // Declare a pointer to member.

class X
{
    int a;
    char b;
    float c;

    public:
    static PtrToMemberInt   getAPtr()
    {
        return &X::a;
    }

    int d;
};

int main()
{
    // For private members you need to use a public method to get the address.
    PtrToMemberInt aPtr = X::getAPtr();

    // For public members you can take the address directly;
    PtrToMemberInt dPtr = &X::d;


    // Use the pointer like this:
    X   a;
    a.*aPtr = 5;
    a.*dPtr = 6;
}
Martin York
True, but the questioner has commented to say that he's using the offset to go in the opposite direction - from the field to the object. Standard intrusive list node, and as far as I'm aware pointer-to-member doesn't help with that.
Steve Jessop
A: 

To add to Martins answer, as stated in "Design and Evolution of C++" by Stroustrup (section [13.11]):

pointers to data members have proven a useful way of expressing the layout of a C++ class in an implementation [Hübel, 1992]

Sandeep has been so kind as to convert the original paper and make it available on http://sandeep.files.wordpress.com/2008/07/ps.pdf

Note that the implementation described predated C++ RTTI, but I occasionally still use the pointer-to-member stuff.

Peter Hübel