Портативный способ вычисления указателя на всю структуру с помощью указателя на поле, объявленное внутри структуры (он же макрос CONTAINING_RECORD)

Известный макрос CONTAINING_RECORD() определен, например, в Winnt.h:

#define CONTAINING_RECORD(address, type, field) ((type *)( \
                                              (PCHAR)(address) - \
                                              (ULONG_PTR)(&((type *)0)->field)))

или во FreeBSD:

#define CONTAINING_RECORD(addr, type, field)    \
      ((type *)((vm_offset_t)(addr) - (vm_offset_t)(&((type *)0)->field)))

или в 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)); })

и, конечно, во многих других местах по всему миру.

Однако я сомневаюсь, что они соответствуют стандарту.

Исходники Boost (boost_1_48_0/boost/intrusive/detail/parent_from_meber.hpp) скорее разочаровали меня - в них есть 3 случая #ifdef PARTICULAR_COMPILER:

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
}

Второй случай (#if defined GNUC and others) кажется наиболее распространенным, но я не уверен, что арифметика указателей с "нулевым инициализированным" родителем хорошо определена (?)

Итак, мои вопросы:

  1. Является ли хотя бы одна из реализаций макроса CONTAINING_RECORD aka container_of стандартно совместимой?

  2. Если нет, то существует ли стандартно совместимый способ вычисления указателя на всю структуру по указателю на поле, объявленное внутри структуры?

  3. Если нет, то существует ли практически переносимый способ сделать это?

Если ответы отличаются для C и C++, то меня интересуют оба случая.

6
задан undur_gongor 23 November 2011 в 10:29
поделиться