“TYPEOF” and “CONTAINER_OF”
“typeof” in the kernel
Many dynamic programming language like Javascript has dynamic way of determining the data type with syntax similar to typeof. In C code, a compiler extension provides a way to use typeof. It is recommended to compile source code with -std=gnu99 (rather -std=c99) for such a feature. In the kernel code, typeof has been widely used. Here is an example:
#ifndef _TYPES_H_
#define _TYPES_H_
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef short s16;
typedef int s32;
typedef long long s64;
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
#define max(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; })
#endif /* _TYPES_H_ */
In the example, the statement typeof(x) _x = (x) could be explained using a specific type say int. That is to say, if x has the int type, then typeof(x) _x = (x) equals to int _x = (x), which creates a variable and assigned by the value of x. There is one line (void) (&_x == &_y) worth noting. This line basically checks if the type _x and type _y are the same. If not, the compile would throw out a warning “comparison of distinct pointer types.” Alternatively, there is way of doing min and max without using typeof.
#define min_t(type, x, y) ({ \
type __min1 = (x); \
type __min2 = (y); \
__min1 < __min2 ? __min1: __min2; })
#define max_t(type, x, y) ({ \
type __max1 = (x); \
type __max2 = (y); \
__max1 > __max2 ? __max1: __max2; })
Considering the following code for different implementation of min. We could find out why we need temp variables (like _x and _y) to store inputs. A function should not worry about such variables, as the arguments that passed through are already copies of the inputs.
#include <stdio.h>
#define min(x ,y) ({ \
(x) < (y) ? (x) : (y); })
#define min_2(x ,y) ({ \
typeof(x) _x = x; \
typeof(y) _y = y; \
(_x) < (_y) ? (_x) : (_y); })
int get_min(int x, int y)
{
return (x < y) ? x : y;
}
int main(int argc, char *argv[])
{
int a = 2, b = 3;
int c = 4, d = 5;
int e = 5, f = 6;
int g = 5, h = 6;
printf("case 1: %d\r\n", min(a, b));
printf("case 2: %d, c: %d, d: %d\r\n", min(c++, d++), c, d);
printf("case 3: %d, e: %d, f: %d\r\n", min_2(e++, f++), e, f);
printf("case 4: %d, g: %d, h: %d\r\n", get_min(g++, h++), g, h);
return 0;
}
Execution results:
There are some warnings during compiling:
test.c:26:51: warning: unsequenced modification and access to 'g' [-Wunsequenced]
printf("case 4: %d, g: %d, h: %d\r\n", get_min(g++, h++), g, h);
^ ~
test.c:26:56: warning: unsequenced modification and access to 'h' [-Wunsequenced]
printf("case 4: %d, g: %d, h: %d\r\n", get_min(g++, h++), g, h);
^ ~
2 warnings generated.
case 1: 2
case 2: 5, c: 6, d: 6 // The ++ results appeared to be wrong. Expect c == 5.
case 3: 5, e: 6, f: 7
case 4: 5, g: 6, h: 7
“container_of” in the kernel
The definition could be found in include/linux/kernel.h. Briefly speaking, the purpose of container_of is to get the structure pointer by a pointer of the member of that structure.
/**
* 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) );})
/* The definition of offsetof */
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
The definition of offsetof gives the address of the MEMBER and force the address to be in size_t formatted. In container_of, the argument member is the name of the member rather a type, and hence we need typeof to determine the type. A compilation error would occur if member is not a valid member of the structure. Some usage samples of offset_of and container_of are presented as below. To prove “&((type *)0)->member” works as expected, testPtr has been created. Basically, “&((TestStruct *)0)->member” is equal to “&testPtr->member”. Since the start address of the structure is 0, the address of a member naturally becomes the offset. The offset of member_0 is 0x0 as member_0 is the initial element, and member_1 is 0x8 as “sizeof(member_0)” (i.e., sizeof(unsigned long)) equals to 8. In the end, use “char *” to format the member’s address and getting the initial address by subtract the offset value.
#include <stdio.h>
#define offsetof(TYPE, MEMBER) \
((size_t)&((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
typedef struct _TestStruct {
unsigned long member_0;
char member_1;
} TestStruct;
int main(int argc, char *argv[])
{
TestStruct test;
TestStruct *testPtr = NULL;
test.member_0 = 5;
test.member_1 = 'c';
printf("test addr :%p\r\n", &test);
printf("member_0 addr :%p\r\n", &test.member_0);
printf("member_1 addr :%p\r\n", &test.member_1);
printf("&((TestStruct *)0)->member_0: %p\r\n",
&((TestStruct *)0)->member_0);
printf("&testPtr->member_1: %p\r\n",
&testPtr->member_1);
printf("offsetof member_0: 0x%zx\r\n",
offsetof(TestStruct,
member_0));
printf("offsetof member_1: 0x%zx\r\n",
offsetof(TestStruct,
member_1));
printf("container_of member_0: %p\r\n",
container_of(&test.member_0,
TestStruct,
member_0));
printf("container_of member_1: %p\r\n",
container_of(&test.member_1,
TestStruct,
member_1));
}
The results are:
test addr :0x7fff5aaa0a80
member_0 addr :0x7fff5aaa0a80
member_1 addr :0x7fff5aaa0a88
&((TestStruct *)0)->member_0: 0x0
&testPtr->member_1: 0x8
offsetof member_0: 0x0
offsetof member_1: 0x8
container_of member_0: 0x7fff5aaa0a80
container_of member_1: 0x7fff5aaa0a80