[cpp deep dive] 成员变量指针 - `Type Scope ::* mem_ptr`

这应该是个十分tricky的东西,但tricky意味着冷门/低可读性.
本文主要关心:

  • Type Scope ::* mem_ptr是什么?(成员变量指针)
  • 如何用它?
  • 应用场景如何?

参考 - C++: Pointer to class data member


好吧,起因是我想写一个“高端”的数据结构,简单地说就是把内核里侵入式数据结构的思想拿出来用。比如,我要弄一个BST(binary search tree),需要有个node结构,每个node又需要两个域用来指向左/右孩子;我说不,我用一个node_ptr类来专门管理这种指向关系,node_ptr类包含2个node_ptr*类型的域,然后在原来node里只要包一个node_ptr进来即可。实际上node_ptr完成了二叉树结点的所有逻辑,而node只是数据域而已.(参考内核链表)

那么一个关键的问题就是,内核中的container_of你要如何来实现呢?(即,你要如何通过node_ptr来访问数据域呢?)

naive solution

有人说,直接搬出来就好了哇~~.如下:

  1 #ifndef MACRO_HACKIN_H                                                          
  2 /**                                                                             
  3 * Container_of - cast a member of a structure out to the containing structure   
  4 * @ptr:        the pointer to the member.                                       
  5 * @type:       the type of the container struct this is embedded in.            
  6 * @member:     the name of the member within the struct.                        
  7 *                                                                               
  8 *                                                                               
  9 *   #define Container_of(ptr, type, member) ({\                                 
 10 *   const __typeof( ((type *)0)->member ) *__mptr = (ptr);\                     
 11 *   (type*)((char *)__mptr - Offsetof(type,member) );}) Another hacking in kernel.
 12 **/                                                                             
 13                                                                                 
 14 #define GET_ARR_LENGTH(ArrName) (sizeof(ArrName) / sizeof(ArrName[0]))          
 15 //Array's length hacking                                                        
 16                                                                                 
 17 #define Offsetof(Type,Member) (&((Type*)0)->Member)                             
 18 //priority: '&' < {'.', '->'}                                                   
 19                                                                                 
 20 #define Container_of(ptr, type, member) (type*)((char *)ptr - (unsigned int)Offsetof(type,member))
 21 //Another hacking in kernel.                                                    
 22                                                                                 
 23                                                                                 
 24 #endif                                                                                                                                                                                                                                                                   

然后用宏来实现类成员名的替换.
不这样做的原因有:

  1. 这些都是宏实现的,在c++里不推荐。
  2. 直接弄出来太low,不够c++,本质上还是个c。

how to do it in more c++ way?

在google上搜了一下,知道了c++有个feature是通过成员变量指针来访问成员,然后确实c++的container_of也是这么做的.甚至我觉得配合上template + object oriented,搞出来比上面的还吊。

可以参考下面这个链接,我改了一下,代码在下面.
A portable way to calculate pointer to the whole structure using pointer to a field declared inside the structure (aka CONTAINING_RECORD macro)

逻辑是这样的,我需要通过node_ptr来访问数据域node,并且node_ptr是node的一个成员,如果用上面的方法,是在预处理器阶段把Type替换成了node。

  • c++的成员变量指针
    假设node类如下:
class node{
  int k;
  int v;       //data field
  node_ptr lr; //pointer field
};

node_ptr node::* member = &node::lr;,该声明 表示 member变量是个指针,指向的类型是node中的node_ptr类型,并且赋值为lr域的地址(lr是node_ptr类型).
使用时是跟.->之后,前面跟个node实例或node指针.如:
node x; x.*member = ...;等价于node x; x.lr = ...;.
或者
node x; (&x)->*member = ...;等价于 node x; (&x)->lr = ...;.

  • ok, 再加上模板特性和搞个node_ptr类,简直掉渣天了.具体自己感受下,是不是很c++ ?
  1 #include "common.h"                                                                                                                                                                                                                                                       
  2 //example 1                                                                     
  3 //kernel's `container_of` in c++                                                
  4 /*                                                                              
  5  * REQUIED: class T have a field `node_ptr`                                     
  6  * */                                                                           
  7 template                                                               
  8 struct node_ptr{                                                                
  9     struct node_ptr* l;                                                         
 10     struct node_ptr* r;                                                         
 11     size_t offsetof(const node_ptr T::*member);                                 
 12     T* container_of(const node_ptr T::*member);                                 
 13     node_ptr():l(NULL),r(NULL){ }                                               
 14 };                                                                              
 15                                                                                 
 16 /*                                                                              
 17  * get the offset of me in class T.                                             
 18  * */                                                                           
 19 template                                                               
 20 size_t node_ptr::offsetof(const struct node_ptr T::* member){             
 21     return (size_t) &(reinterpret_cast(0)->*member);                        
 22 }                                                                               
 23                                                                                 
 24 /*                                                                              
 25  * get the pointer to who contains me.                                          
 26  * */                                                                           
 27 template                                                               
 28 T* node_ptr::container_of(const node_ptr T::*member) //<-----------`::` followed by `*` 
 29 {                                                                               
 30     return (T*)(reinterpret_cast(this) - offsetof(member));              
 31 }                                                                               
 32                                                                                 
 33 class node{                                                                     
 34     public:                                                                     
 35         node(int k, int v):key(k),val(v){                                       
 36         }                                                                       
 37         node(int k,int v, node* l, node* r):key(k),val(v){                      
 38             lr.l = &(l->lr);                                                    
 39             lr.r = &(r->lr);                                                    
 40         }                                                                       
 41         int key;                                                                
 42         int val;                                                                
 43         struct node_ptr lr;                                               
 44 };                                                                              
 45                                                                                 
 46 int main()                                                                      
 47 {                                                                               
 48 //example 1                                                                     
 49     node *l = new node(99,0);                                                   
 50     node *l1 = new node(0,1);                                                   
 51     node *fa = new node(1,1,l,l1);                                              
 52                                                                                 
 53     cout<< (fa->lr.l->container_of(&node::lr))->key << endl;                    
 54     return 0;                                                                   
 55 }

(node_ptr类里可以完成各种类型的tree or list)

-----Update 7.24---------
levelDB::Benchmark中有这么一句:


[cpp deep dive] 成员变量指针 - `Type Scope ::* mem_ptr`_第1张图片
Paste_Image.png

表示“method是个指针变量,指向的是个函数,该函数的返回类型是void,该函数位于Benchmark域内,该函数的参数只有一个,且类型是ThreadState*”

你可能感兴趣的:([cpp deep dive] 成员变量指针 - `Type Scope ::* mem_ptr`)