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
*/