Converts an intrusive member back into the data type that stores it. A common use case for this macro is for intrusive nodes.
The following example uses a linked list node API that can only work with linked list node types to remain portable. This macro allows the linked list node to be converted back into the user’s node type, allowing any data to be stored without dynamic memory allocation:
/*------------ Linked List API - linked_list.h ------------*/structlinked_list_node{/* Members specific to data structure... */};/* API functions can only work with linked_list_node type to remain portable. */externstructlinked_list_node*linked_list_next(structlinked_list_node*n);/*-------------------- User's main.c ----------------------*/#include"linked_list.h"structuser_node{/* User data. */inta;intb;/* Make linked_list_node intrusive so we can store any data. */structlinked_list_nodenode;/* More user data. */intc;};/* Make user node. Assume everything is initialized alreadyand it's in a list. Use linked list API. Convert returnedvalue back into user's node type. */structuser_noden1;structlinked_list_node*next=linked_list_next(&n1.node);structuser_node*me=ECU_CONTAINER_OF(next,structuser_node,node);me->a=5;me->b=5;me->c=5;
This macro takes in three parameters:
ptr_ = Pointer to intrusive member. In this case, next.
type_ = User’s data type containing the intrusive member. In this case, structuser_node.
member_ = Name of intrusive member within the user’s type. In this case, node.
The supplied pointer is casted to (uint8_t *) so it is properly decremented, updating the expression to:
structuser_node*me=(uint8_t*)next-X;
The offset ‘X’ can be determined at compile-time using offsetof(). offsetof() is mandated by the C standard since C89, so this is guaranteed to be a portable solution. Substituting this value in for ‘X’ completes the first portion of the expression:
This warning is triggered whenever a pointer cast results in increased alignment requirements. In this case, casting (uint8_t *) to (struct user_node *) triggers the warning because (struct user_node *) must be aligned on a greater byte boundary than (uint8_t *).
Under normal circumstances, this cast is illegal and this warning must be addressed. However for this use case, the cast is always a safe operation. When a (struct user_node) object is defined, the compiler allocates memory starting at an address that satisfies the alignment requirements of type (struct user_node). The CONTAINER_OF() macros return this raw starting address, which is guaranteed to be aligned according the requirements of (struct user_node) since this was originally done by the compiler. Therefore the updated expression becomes:
Verifies, at compile-time, that derived class correctly inherits base class via C-style inheritance. Returns true if this condition is satisfied. False otherwise:
structmy_base{inta;};structmy_derived_correct{structmy_basebase;/* Use C-style inheritance by declaring my_base as FIRST member. */intb;intc;};structmy_derived_incorrect{intb;structmy_basebase;intc;};/* Passes. */ECU_STATIC_ASSERT((ECU_IS_BASEOF(base,structmy_derived_correct)),"struct my_base must be first member.");/* Compilation error. */ECU_STATIC_ASSERT((ECU_IS_BASEOF(base,structmy_derived_incorrect)),"struct my_base must be first member.");
Verifies, at compile-time, that supplied type is signed. Returns true if this condition is satisfied. False otherwise:
/* Passes. */ECU_STATIC_ASSERT((ECU_IS_SIGNED(int8_t)),"type must be signed.");ECU_STATIC_ASSERT((ECU_IS_SIGNED(float)),"type must be signed.");/* Fails. */ECU_STATIC_ASSERT((ECU_IS_SIGNED(uint8_t)),"type must be signed.");
Verifies, at compile-time, that supplied type is unsigned. Returns true if this condition is satisfied. False otherwise:
/* Passes. */ECU_STATIC_ASSERT((ECU_IS_UNSIGNED(uint8_t)),"type must be unsigned.");/* Fails. */ECU_STATIC_ASSERT((ECU_IS_UNSIGNED(int8_t)),"type must be unsigned.");ECU_STATIC_ASSERT((ECU_IS_UNSIGNED(float)),"type must be unsigned.");