使用指针的字段来计算指针整体结构的便携式方式结构内声明(又名CONTAINING_RECORD宏) [英] A portable way to calculate pointer to the whole structure using pointer to a field declared inside the structure (aka CONTAINING_RECORD macro)

查看:112
本文介绍了使用指针的字段来计算指针整体结构的便携式方式结构内声明(又名CONTAINING_RECORD宏)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有是限定,例如公知的CONTAINING_RECORD()宏,在WINNT.H:

 的#define CONTAINING_RECORD(地址,类型,字段)((*型)(\\
                                              (PCHAR)(地址) - \\
                                              (ULONG_PTR)(及((类型*)0) - &GT字段)))

或FreeBSD的:

 的#define CONTAINING_RECORD(地址,类型,字段)\\
      ((类型*)((vm_offset_t)(地址) - (vm_offset_t)(及((类型*)0) - &GT字段)))

或Linux的:

 的#define offsetof(TYPE,MEMBER)((为size_t)及((TYPE *)0) - > MEMBER)
#定义container_of(PTR,类型,成员)({\\
      常量的typeof(((键入*)0) - >成员)* __mptr =(PTR); \\
      (类型*)((字符*)__ mptr - offsetof(类型,成员)); })

和,当然,在全球许多其他地方。

不过,我怀疑他们是符合标准。

Boost源代码(boost_1_48_0 /升压/侵入/细节/ parent_from_meber.hpp),而
disapoint我 - 他们有3 #IFDEF PARTICULAR_COMPILER情况:

 模板<父类,类成员>
内联的std :: ptrdiff_t的offset_from_pointer_to_member(const成员父:: * ptr_to_member)
{
   //指针成员的实现是编译器相关的。
   #如果定义(BOOST_INTRUSIVE_MSVC_COMPLIANT_PTR_TO_MEMBER)
   // MSVC的编译器使用他们的前32位作为偏移量(即使是在64位模式)
   回*(常量的boost :: int32_t *)(无效*)及ptr_to_member;
   //这个工程用gcc,MSVC,AC ++,ibmcpp
   #elif指令定义(__ GNUC__)||定义(__ HP_aCC)||定义(BOOST_INTEL)|| \\
         定义(__ IBMCPP__)||定义(__ DECCXX)
   常量母公司* const的父= 0;
   为const char * const成员= reinter pret_cast<为const char *>(及(父 - > * ptr_to_member));
   返回的std :: ptrdiff_t的(成员 - reinter pret_cast<为const char *>(父));
   #其他
   //这是传统的C-前的做法:__MWERKS__,__DMC__,__SUNPRO_CC
   回报(*(常量的std :: ptrdiff_t的*)(无效*)及ptr_to_member) - 1;
   #万一
}

第二个案例(#如果定义GNUC及其他))似乎是最常见的,但我不知道以零initalized父指针运算是明确的(?)

所以我的问题是:


  1. 是CONTAINING_RECORD的至少一个又名container_of宏实现符合标准的?


  2. 如果没有,存在一个标准兼容的方式使用指针结构内声明的字段来计算指向整个结构?


  3. 如果没有,存在一个几乎可移植的方式做到这一点?


如果答案为C和C ++不同,我感兴趣的是这两种情况下。


解决方案

  1. 没有,没有你已经给了一个人的符合。取消对NULL指针没有,的typeof 是不是和({...})前pressions都没有。


  2. 是的,linux的事情第二部分重写正常(类型*)((字符*)(PTR) - offsetof(类型,成员))符合。 ( offsetof 在标准中被定义)


  3. 2见


据我所知,这一切都是有效的C和C ++

编辑:的而linux的事情试图通过如果一个指针成员,指针在参数是赋值兼容的检查,以额外的安全性添加到宏。至于我可以看到gcc的扩展名的typeof 是在C这样的做法是至关重要的。

通过C99,您可以使用但一个复合文字有一个稍差检查通过执行类似

 (类型){。成员= *(PTR)}

但是这只会告诉你,如果的类型* PTR 是分配兼容成员。例如,如果 PTR 浮法* 和成员双击此仍然会正常工作。

在任何情况下,类型检查只给你假的安全性。你必须是pretty确保您的 PTR 真正来自内部键入来使用它。要小心。

编辑:的作为BERT - 扬在下面他的意见,在C ++的言论,当有虚拟继承,在 offsetof 宏的根本问题成员的偏移量不能在编译时确定。我不相信,一个容器宏的present想法,然后让在这样的背景下任何意义。是的非常的注意。

There is the well known CONTAINING_RECORD() macro defined, for instance, in Winnt.h:

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

or in FreeBSD:

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

or in 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)); })

and, surely, in many other places worldwide.

However, I doubt they are standard compliant.

Boost sources (boost_1_48_0/boost/intrusive/detail/parent_from_meber.hpp) rather disapoint me - they have 3 #ifdef PARTICULAR_COMPILER cases:

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
}

The second case (#if defined GNUC and others) ) seems most common but I'm not sure that pointer arithmetic with "zero initalized" parent is well-defined (?)

So my questions are:

  1. Is at least one of CONTAINING_RECORD aka container_of macro implementations standard compliant?

  2. If not, does there exist a standard compliant way to calculate pointer to the whole structure using pointer to a field declared inside the structure?

  3. If not, does there exist a practically portable way to do it?

If answers differ for C and C++, I'm interested in both cases.

解决方案

  1. No, none of the one's you have given is compliant. Dereferencing a null pointer isn't, typeof isn't and ({ ... }) expressions aren't.

  2. yes, the second part of the linux thing rewritten properly (type *)((char *)(ptr) - offsetof(type, member)) is compliant. (offsetof is defined in the standard)

  3. see 2

AFAIK, all this is valid for C and C++

Edit: The linux thing tries to add additional security to the macro by checking if a pointer to the member and the pointer in the argument are assignment compatible. As far as I can see the gcc extension typeof is essential for such an approach in C.

With C99, you could though use a "compound literal" to have a somewhat weaker check by doing something like

(type){ .member = *(ptr) }

but this would only tell you if the type of *ptr is assignment compatible to member. E.g if ptr would be float* and member double this would still work.

In any case, the check of the types only gives you false security. You must be pretty sure that your ptr really comes from inside type to use this. Be careful.

Edit: As bert-jan remarks in his comments below, in C++, when there is virtual inheritance, the offsetof macro has the fundamental problem that the offset of member cannot be determined at compile time. I am not convinced that the present idea of a container macro then makes any sense in such a context. Be extremely careful.

这篇关于使用指针的字段来计算指针整体结构的便携式方式结构内声明(又名CONTAINING_RECORD宏)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆