|
The Linux kernel contains a very useful macro named "container_of" that is used extensively in back-casting a data structure to its containing data structure. This article includes a simple program that illustrates how this macro is used, and explains why it is so useful.
If you do a lot of C programming, this program is worth figuring out . The program source can also be downloaded from the following location: http://bec-systems.com/linux/linux_container_of_example.c /* test code to illustrate use of Linux kernel container_of macro * * Copyright (c) 2008 Cliff Brake, BEC Systems LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */
#include <stdio.h> #include <string.h> #include <stdlib.h>
/* This program illustrates how the container_of macro works. * The container of macro is very useful in multi layered * software systems where you have progressivly more detailed * software layers. Below is an example of a bus layer, * and then a device layer where a number of different * devices might register with the bus. * The device registers itself with the bus subsystem, and * then the bus subsystem makes a callback into the device. * Normally if there are multiple devices registered, the * bus subsystem must store and pass a device structure * when making callbacks. With the container_of macro, this is * no longer necessary, and the bus subsystem only has to * know about one generic device structure, and does not need visibility * into lots of different device structures, or do tricks * by casting void pointers, etc. With the container_of macro * we can backcast from the generic data structure, to the containing * datastructure. This forces good separation of code in that * that bus layer cannot modifiy data structures that are specific * to the device layer. * */
/** * (from Linux kernel source) * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/*========================================================== * BUS layer code *==========================================================*/
/* generic bus device structure */ struct bus_device { int general_device_param_a; int general_device_param_b; void (*device_callback)(struct bus_device * bd); };
/* the following is a global list of * devices that have registered with the * bus subsystem. Normally this would * be something like a dynamic linked list. */ struct bus_device * bd_list[5];
/* function to register a device with the bus */ void register_with_bus(struct bus_device * bd) { /* since this example only deals with one * device, will put it in slot 0 */
bd_list[0] = bd; }
void start_bus() { int i; struct bus_device * bd;
/* make callbacks to all devices on bus */ for (i=0;i<sizeof(bd_list)/sizeof(bd_list[0]);i++) { bd = bd_list[i]; if (!bd) continue; /* call device callback with generic * bus device structure */ bd->device_callback(bd); } }
/*========================================================== * device X specific code * this would normally be in a different module *==========================================================*/
/* structure that holds device X specific stuff, as well as * generic bus_device structure */ struct device_x { int device_x_specific_param_a; int device_x_specific_param_b; struct bus_device bd; };
void device_x_callback(struct bus_device * bd) { /* if we know the structure type that contains the bus_device structure, * we can extract a pointer to the containing structure using the container_of * macro */
/* ptr type member */ struct device_x * devx = container_of(bd, struct device_x, bd);
/* the above statement expands to * struct device_x * devx = ( * { * const typeof( ((struct device_x *)0)->bd ) *__mptr = (bd); * (struct device_x *)( (char *)__mptr - ((size_t) &((struct device_x *)0)->bd) ); * } * ); */
printf("device_x_callback called!, device_x_specific_param_a = %i\n", devx->device_x_specific_param_a); }
void device_x_init() { /* dynamically allocate structures */ struct device_x * devx = malloc(sizeof(*devx)); memset(devx, 0, sizeof(*devx));
/* set a parameter in the device_x structure so * we can test for this in the callback */ devx->device_x_specific_param_a = 1001;
/* set up callback function */ devx->bd.device_callback = device_x_callback;
/* we register the generic bus device structure * as the bus layer does not need to know * about the device_x stucture. Note, the * devx structure is not stored anywhere, yet * its location is being preserved without * specifically passing it to the bus * layer. */ register_with_bus(&devx->bd); }
int main() {
/* test the above system */
/* first, initialize device_x */ device_x_init();
/* now, start the bus. This should make * a callback into the device_x */ start_bus(); }
/* when run, this program returns: * device_x_callback called!, device_x_specific_param_a = 1001 */
|