这应该是个十分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
然后用宏来实现类成员名的替换.
不这样做的原因有:
- 这些都是宏实现的,在c++里不推荐。
- 直接弄出来太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中有这么一句:
表示“method是个指针变量,指向的是个函数,该函数的返回类型是void,该函数位于Benchmark域内,该函数的参数只有一个,且类型是ThreadState*”