container_of マクロ
これは
Beautiful Code に記述がある。引用しつつ意味合いをログ。
最初に
スーパークラスとしての struct device 型についての解説あり。ここはオミット。ざくっと書いてしまうと container_of は以下のような構造体
struct usb_interface { /* array of alternate settings for this interface, * stored in no particular order */ struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; /* the currently * active alternate setting */ unsigned num_altsetting; /* number of alternate settings */ int minor; /* minor number this interface is * bound to */ enum usb_interface_condition condition; /* state of binding */ unsigned is_active:1; /* the interface is not suspended */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ struct device dev; /* interface specific device info */ struct class_device *class_dev; int pm_usage_cnt; /* usage counter for autosuspend */ };
のメンバである dev を引数にして云々な処理の中でサブクラスな struct usb_interface 型のポインタだったりメンバだったりが必要になった時に使う模様。
とりあえず container_of マクロの定義を再度
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
typeof とか offsetof とは GNU 拡張なんでしょうか。
という事で
google 先生に聞いてみたら以下の記述
gcc拡張文法ではtypeof演算子で変数の型を表すことができ、さらにそれで別の変数を宣言することができます。
次のようにすると、変数yは変数xの型で宣言されます。
int x;
typeof(x) y; // GCC Extension
GCC some extensions より引用
成程。次は offsetof ですが以下。
TYPEの開始アドレスからMEMBERの開始アドレスまでのバイト数
offsetof - GCC 解読室 Wikiより引用
ええと、マクロの定義は以下のようになってます。include/linux/stddef.h にて定義??
#ifdef __KERNEL__ enum { false = 0, true = 1 }; #undef offsetof #ifdef __compiler_offsetof #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) #else #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #endif /* __KERNEL__ */
ちょっと余計な部分もあるかも、ですが。
しかし
((size_t) &((struct usb_interface *)0)->dev)
って何だよ、と。0 から始まるナニのメンバなアドレス、になるのか。わははは的。
てーコトで
再度 container_of マクロをニラみつつ、昨晩のエントリを云々と思ったらソースファイルに関する記述が無い (とほほ
ええと iget は include/linux/fs.h との事。そこから順に
- iget_locked() は fs/inode.c
- ifind_fast() も fs/inode.c
- find_inode_fast() も fs/inode.c
で、その中で hlist_entry マクロがナニ。このマクロは include/linux/list.h にて定義されている模様。
inode = hlist_entry(node, struct inode, i_hash);
な形で呼び出されてて hlist_entry マクロの定義が以下
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
展開
とりあえず当てハメてみる。
inode = ({ const typeof( ((struct inode *)0)->i_hash) *__mptr = (node); \ (struct inode *)( (char *)__mptr - offsetof(struct inode, i_hash) ); });
ええと struct inode 型の i_hash は
struct hlist_node i_hash;
な定義。もう少し評価してみると以下??
inode = ({ const struct hlist_node *__mptr = (node); \ (struct inode *)( (char *)__mptr - offsetof(struct inode, i_hash) ); });
ちょっと途中を略しますが、i_hash 属性なアドレスから struct inode 型なオブジェクトの先頭アドレスを求める演算になりますな。
確かにこれ使えばリストなポインタ持ってる属性からその親オブジェクト (って言い方微妙) の先頭アドレスが求まりますな。
抽象化
スーパークラスの参照 (正確にはポインタ) を渡してそこから具体的なオブジェクトを求める処理をこうした形で、という事も凄いんですがメンバにリストへのポインタ持たせておいてリストを手繰りながらそのオブジェクトの先頭ポインタが求められる、というのもなかなか凄い。