Es gibt das bekannte Makro CONTAINING_RECORD(), das zum Beispiel in Winnt.h definiert ist:
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
oder unter FreeBSD:
#define CONTAINING_RECORD(addr, type, field) \
((type *)((vm_offset_t)(addr) - (vm_offset_t)(&((type *)0)->field)))
oder unter Linux:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
und sicherlich auch an vielen anderen Orten weltweit.
Ich bezweifle jedoch, dass sie der Norm entsprechen.
Boost-Quellen (boost_1_48_0/boost/intrusive/detail/parent_from_meber.hpp) eher enttäuschten mich - sie haben 3 #ifdef PARTICULAR_COMPILER Fälle:
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
}
Der zweite Fall (#if definiert GNUC und andere) ) scheint am häufigsten, aber ich bin nicht sicher, dass Zeiger Arithmetik mit "Null initalized" Elternteil ist gut definiert (?)
Meine Fragen lauten also:
-
Ist mindestens eine der Implementierungen des Makros CONTAINING_RECORD aka container_of standardkonform?
-
Wenn nicht, gibt es eine standardkonforme Methode zur Berechnung des Zeigers auf die gesamte Struktur unter Verwendung des Zeigers auf ein innerhalb der Struktur deklariertes Feld?
-
Wenn nicht, gibt es eine praktisch übertragbare Möglichkeit, dies zu tun?
Wenn sich die Antworten für C und C++ unterscheiden, bin ich an beiden Fällen interessiert.