Ethan
Ethan

Reputation: 1266

Preprocessor Macro Explanation?

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)))

Upvotes: 0

Views: 202

Answers (2)

anishsane
anishsane

Reputation: 20980

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 by offsetof.

e.g. consider below struct:

struct abcd{
int a; // 4 bytes
int b; // another 4 bytes
char c;// 1 byte
}

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).

Upvotes: 1

technosaurus
technosaurus

Reputation: 7812

just think of macros as sed for C

any place where you see QUEUE_ENTRY(a, b, c) you would get:

((b *)((uint8_t*)(a) - offsetof(b, c)))

all of these replacements are done before it is ever compiled

Upvotes: 1

Related Questions