¿Cuándo usas container_of macro?

Sé lo que hace la macro.

En muchos códigos de nivel de kernel, a menudo se utiliza para recorrer la lista enlazada.

Quiero encontrar otros casos útiles.
¿Cuándo usa container_of o la macro CONTAINING_RECORD ?
¿Cuándo es la macro extremadamente útil?

container_of permite simplificar sus estructuras de datos al omitir los punteros a las estructuras primarias.

Se utiliza dentro de la implementación de la lista vinculada, de modo que el nodo de la lista puede ser un elemento de cualquier estructura, y cualquier persona puede encontrar la estructura principal sin llevar un puntero explícito.

Otro ejemplo es struct work_struct . Una función de trabajo de cola de trabajo recibe una estructura_trabajo como un argumento, y solía tener una carga útil de “datos” genérica. Este valor de datos se eliminó , lo que hace que la estructura sea más pequeña, ya que la función de trabajo puede llamar a container_of para encontrar su estructura principal.

Es una forma de evitar el hecho de que C no tiene generics ni plantillas.

Desea una lista vinculada genérica, por lo que solo coloca los punteros dentro del propio nodo (para poder abstraer la administración de la estructura), luego use CONTAINING_RECORD para encontrar el rest de los datos en su propio código, por ejemplo:

 struct Node { struct Node *prev, *next; } //Now you can define functions that operate on a generic struct Node* struct Item { int myData; struct Node* node; //this would point to the 'node' member of another Item } 

Ahora, dado un struct Node , puedes encontrar su Item diciendo:

 CONTAINING_RECORD(ptr, Item, node) 

Para el futuro searche (r) s: esta es la mejor explicación que he encontrado hasta ahora:

http://psomas.wordpress.com/2009/07/01/weird-kernel-macros-container_of/

Básicamente (cita):

“Ahora podemos entender (al menos parcialmente) lo que hace la macro. Declara un puntero al miembro de la estructura a la que apunta ptr , y le asigna ptr. Ahora __mptr apunta a la misma dirección que ptr . Luego obtiene la desplazamiento de ese member dentro de la struct , y lo resta de la dirección real del miembro de la struct 'instance'(ie __mptr) . La struct 'instance'(ie __mptr) (char *)__mptr es necesaria, por lo que la ‘aritmética de punteros’ funcionará según lo previsto, es decir, restar de __mptr exactamente los bytes (size_t) que offsetof ‘retornan’ “.

y, también, otros dos consejos significativos (cita):

“En este punto, realmente no puedo entender por qué no pudimos usar el puntero ptr directamente. Podríamos omitir la primera línea, y la macro podría ser

 #define container_of(ptr, type, member) (type *)( (char *)(ptr) - offsetof(type,member) ) 

ptr se usa solo una vez, no tenemos que preocuparnos por los efectos secundarios. Tal vez solo sea una buena práctica de encoding “.

y, la edición posterior de la publicación original (citado):

“Aparentemente, la primera línea está ahí para la ‘comprobación de tipo’. Asegura que el type tiene un miembro llamado member (sin embargo, esto también se hace mediante la offsetof macro, creo), y si ptr no es un indicador del tipo correcto ( el tipo del member ), el comstackdor imprimirá una advertencia, que puede ser útil para la depuración “.

Ajusta un puntero a un miembro de una estructura a un puntero a la estructura contenedora; esto se usa de varias maneras en el kernel, el más común podría describirse como un downcast con un offset estático donde la estructura externa se deriva (por inclusión) del interno, y el llamador invoca un método en el objeto interno, que es luego se despacha a un método en el objeto exterior.

Bueno, eso, sin el soporte OO del comstackdor.