[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*”

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容