My teacher is having us use the following preprocessor macro in implementing a linked queue in C. The idea is you make your queue generic by having it hold no data, then you have a wrapper struct elsewhere which holds the node in the queue, and the piece of data belonging to it.
The macro below takes in a node from the queue (i.e. node), the type of the wrapper struct (i.e. struct Wrapper), and name of the queue node element of the Wrapper (i.e. qnode (Wrapper has an element called qnode)).
Then the macro returns the struct Wrapper that the node passed in is contained in.
So a call would look like this:
queue_entry(node, struct Wrapper, qnode)
This is EXTREMELY COOL in my eyes, and it works fine, (seeing as how my teacher wrote it, it’d better!).
But I was hoping someone could explain to me how it actually works? Because I’m at a loss for what is actually going on behind the scenes.
The Macro:
#define queue_entry(NODE, STRUCT, MEMBER) \
((STRUCT *)((uint8_t*)(NODE) - offsetof(STRUCT, MEMBER)))
The member elements in C struct have fixed difference in their address. This difference gets defined at the compile time itself.
You can get this difference in address by using
&(struct_object.member) - &struct_object. This is what is returned byoffsetof.e.g. consider below struct:
Then
offsetof(struct abcd,c)would return 8.offsetof(struct abcd,a)would be 0 & so on. The ‘padding‘ or ‘alignment’ within the struct also plays the role, in deciding the offset. However, this is decided at the compile time, not run time.Hence, if you have address of a member (char c in our example), but not the structure, you can obtain the address of the parent structure, by subtracting the member address offset, from member address.
In your example, address of member is contained in node. Hence if you subtract member ofset, you will get address of container struct.
In linux kernel source code, same macro is available under the name,
container_of(I forgot the capitalization & underscores if any).