6 Stimmen

Ein portabler Weg zur Berechnung des Zeigers auf die gesamte Struktur unter Verwendung des Zeigers auf ein innerhalb der Struktur deklariertes Feld (auch bekannt als CONTAINING_RECORD-Makro)

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:

  1. Ist mindestens eine der Implementierungen des Makros CONTAINING_RECORD aka container_of standardkonform?

  2. 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?

  3. 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.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X