2

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?

m.s.
  • 16,063
  • 7
  • 53
  • 88
Sipka
  • 2,291
  • 2
  • 27
  • 30

3 Answers3

4

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.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Doesn't the member pointer in the offsetof macro skips the v-table? If so, then why doesn't it work on non-POD types? – Sipka Aug 12 '15 at 10:04
  • my understanding is that `offsetof` is not defined for polymorphic types – Bathsheba Aug 12 '15 at 10:06
  • It's indeed not (http://en.cppreference.com/w/cpp/types/offsetof), however by making the pointer point to the first data member, doesnt it skip the preceeding v-table? Where can the pointer manipulation go wrong? – Sipka Aug 12 '15 at 10:09
  • I don't know. It will probably work once you've skipped over the v-table. But bear in mind that it will not be well-defined. I cannot recommend use of `offsetof` for a polymorphic type in *any* circumstance. – Bathsheba Aug 12 '15 at 10:13
1

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.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
1

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.

Mark Jansen
  • 1,491
  • 12
  • 24
  • You should probably mention that it's MSVC-specific. – Quentin Aug 12 '15 at 09:54
  • I'm also trying to use this with linked lists, and this macro could be used, however I'm trying to create portable code not just Windows specific. – Sipka Aug 12 '15 at 09:55
  • I would hardly call that MSVC specific. MSVC might be bundled with an sdk where it's defined, but for example [Reactos](https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/include/psdk/ntdef.h;hb=c3760ed990ab91afb49a4484ab0809ad32e4e413#l212) also defines it. (And that builds fine with gcc and MSVC – Mark Jansen Aug 12 '15 at 12:10
  • In linux land this is usually called `container_of` – Mark Jansen Aug 12 '15 at 13:42