C++[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<class T>                                                               
  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<class T>                                                               
 20 size_t node_ptr<T>::offsetof(const struct node_ptr<T> T::* member){             
 21     return (size_t) &(reinterpret_cast<T*>(0)->*member);                        
 22 }                                                                               
 23                                                                                 
 24 /*                                                                              
 25  * get the pointer to who contains me.                                          
 26  * */                                                                           
 27 template<class T>                                                               
 28 T* node_ptr<T>::container_of(const node_ptr T::*member) //<-----------`::` followed by `*` 
 29 {                                                                               
 30     return (T*)(reinterpret_cast<char*>(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<node> 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中有这般一句:

Paste_Image.png

表示“method是独指针变量,指向的凡单函数,该函数的归路是void,该函数位于Benchmark域内,该函数的参数就出一个,且项目是ThreadState*”