Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recover Parent from Pointer-to-member

Suppose that we have a pointer-to-class member pointing at a field of a class. We also have a pointer to that specific field in a particular instance of the class. For example, we might have something like this:

class A {
     B inner_object;
}

A* myA = /* ... */
B* ptr = &myA->inner_object;
B A::* memPtr = &A::inner_object;

Is there a way to use ptr and memPtr to recover myA? That is, if we didn't already have an explicit pointer for myA, could we make one out of ptr and memPtr?

like image 655
bcr Avatar asked Jul 29 '11 01:07

bcr


3 Answers

After a decent amount of research...

This is actually done in most industrial intrusive list implementations. It does require some hackery, however.

Boost intrusive structures use the following (and yes, it is implementation specific)

template<class Parent, class Member>
inline const Parent *parent_from_member(const Member *member, const Member Parent::*   ptr_to_member)
{
   return (const Parent*)((const char*)member -
      offset_from_pointer_to_member(ptr_to_member));
}


template<class Parent, class Member>
inline std::ptrdiff_t offset_from_pointer_to_member(const Member Parent::* ptr_to_member)
{
   //The implementation of a pointer to member is compiler dependent.
   #if defined(BOOST_INTRUSIVE_MSVC_COMPLIANT_PTR_TO_MEMBER)
   //msvc compliant compilers use their the first 32 bits as offset (even in 64 bit mode)
   return *(const boost::int32_t*)(void*)&ptr_to_member;
   //This works with gcc, msvc, ac++, ibmcpp
   #elif defined(__GNUC__)   || defined(__HP_aCC) || defined(BOOST_INTEL) || \
     defined(__IBMCPP__) || defined(__DECCXX)
   const Parent * const parent = 0;
   const char *const member = reinterpret_cast<const char*>(&(parent->*ptr_to_member));
   return std::ptrdiff_t(member - reinterpret_cast<const char*>(parent));
   #else
   //This is the traditional C-front approach: __MWERKS__, __DMC__, __SUNPRO_CC
   return (*(const std::ptrdiff_t*)(void*)&ptr_to_member) - 1;
   #endif
}

Essentially the same thing (albeit in C) as is done in the linux kernel to manage intrusive lists, with the container_of macro (but of course ptr-to-members are not used):

#define container_of(ptr, type, member) ({ \
            const typeof( ((type *)0)->member ) *__mptr = (ptr); 
            (type *)( (char *)__mptr - offsetof(type,member) );})
like image 93
bcr Avatar answered Oct 27 '22 00:10

bcr


You don't. A pointer-to-member has no knowledge of any instance of the class that it is a member of. That's why you need an instance anytime you want to access the member through the pointer.

A pointer-to-member is not a pointer. Indeed, it was probably a mistake on the C++ committee's part to even call it a pointer. On many (if not most) implementations, even the sizes of a pointer are not equal to the size of a pointer-to-member. There's no offset tricks you can play here. And even if you found a way, if you parsed the data in a pointer-to-member, it still would be specific to that implementation.

like image 27
Nicol Bolas Avatar answered Oct 26 '22 22:10

Nicol Bolas


You can't.

A pointer to member does not store information about any particular instance.

It knows only a type, and a pointer to a function within that type.

like image 29
Lightness Races in Orbit Avatar answered Oct 27 '22 00:10

Lightness Races in Orbit