Get pointer to enclosing instance from member pointer

334 Views Asked by At

I have a custom class with some data members. I've got a pointer to one of the class' data member, and I would like to have a pointer to its enclosing instance. For example:

class MyClass{
public:
    int a;
    int b;
    virtual ~MyClass(){//because MyClass is not POD type
    }
};

...

int* aptr = ...; //something valid, and i know its a pointer to a MyClass::a member
MyClass* classptr = ?; //how can i get a pointer to the class instance?

The class is not a POD type, so the offsetof macro doesn't always work/it gives a compile warning.

Is it possible to get a pointer to the MyClass instance?

3

There are 3 best solutions below

4
On BEST ANSWER

You cannot do this using well-defined C++ as casting between unrelated types is undefined behaviour.

In reality, you'll probably get away with assuming that the address of the first member of a class is the same as the address of the class offset by the size of a pointer on your system. (This pointer being the implementation of your v-table and that's reasonably consistent across C++ implementations.) Then, if you make some assumptions about the packing of the data members, then you make manual adjustments to your pointer to move from one data member to another. offsetof is another technique and can help you here, but it's still not well-defined in your context.

Either litter your source code with specific compiler assertions (as you are restricting portability), or adopt a different technique. I'd certainly adopt the latter.

Here's some very bad code which shows you how to do it. Consider

struct Foo
{
    virtual ~Foo(){}; /*introduce a v-table*/
    int n;
};

And,

  Foo foo;
  foo.n = 0xdeadbeef; // To test
  int* p = &foo.n; // Suppose this is our pointer.
  char* pp = (char*)(void*)p; // This cast is undefined behaviour.
  pp -= 8; // Skip over 64 bit v-table. More undefined behaviour.
  Foo* ph = (Foo*)(pp); // Yet more undefined behaviour.

ph points to a Foo instance.

0
On

No, it's not possible in standard C++. offsetof is the only portable way to do such a thing, and it's only meant for standard-layout classes. If it doesn't work (or you can't suppress and ignore the warning), you're out of luck.

4
On

CONTAINING_RECORD is the macro that might work for you, it's regularly used with linked lists, where the pointer to the next and previous item are embedded inside another struct.