Skip to content

Instantly share code, notes, and snippets.

@pmj
Created June 17, 2012 15:46
Show Gist options
  • Save pmj/2944912 to your computer and use it in GitHub Desktop.
Save pmj/2944912 to your computer and use it in GitHub Desktop.
Super safe container_of macro for structs
/** genc_container_of(obj, cont_type, member_name)
* Get pointer to struct object from a pointer to one of its members.
* obj - pointer to a struct member, or NULL
* cont_type - structure type of the containing object
* member_name - name of the member of cont_type referenced by obj
*
* Similar to Linux's container_of macro, except it also handles NULL pointers
* correctly, which is to say it evaluates to NULL when the obj is NULL. We also
* try to support non-GCC compilers which don't support the ({ }) expression
* syntax.
*
* Where the obj can be guaranteed to be non-NULL, genc_container_of_notnull()
* may be used. It omits the NULL check and its behaviour is therefore undefined
* if obj is indeed NULL.
*/
#ifndef genc_container_of
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#ifndef __has_extension
#define __has_extension __has_feature
#endif
/* Where possible, make the helper functions const-correct via overloading.
* C++ compilers obviously support it, but so does clang. For pure GCC,
* we have a different solution. */
#if defined(__cplusplus) || __has_extension(attribute_overloadable)
#ifdef __cplusplus
#define GENC_OVERLOADABLE
#else
/* This is clang, basically */
#define GENC_OVERLOADABLE __attribute__((overloadable))
#endif
/* function for avoiding multiple evaluation */
static inline void* GENC_OVERLOADABLE genc_container_of_helper(void* obj, ptrdiff_t offset)
{
return (obj ? ((char*)obj - offset) : NULL);
}
static inline const void* GENC_OVERLOADABLE genc_container_of_helper(const void* obj, ptrdiff_t offset)
{
return (obj ? ((const char*)obj - offset) : NULL);
}
/* function for avoiding multiple evaluation */
static inline const void* GENC_OVERLOADABLE genc_container_of_notnull_helper(const void* obj, ptrdiff_t offset)
{
return ((const char*)obj - offset);
}
static inline void* GENC_OVERLOADABLE genc_container_of_notnull_helper(void* obj, ptrdiff_t offset)
{
return ((char*)obj - offset);
}
#else
/* function for avoiding multiple evaluation */
static GENC_INLINE void* genc_container_of_helper(void* obj, ptrdiff_t offset)
{
return (obj ? ((char*)obj - offset) : NULL);
}
/* function for avoiding multiple evaluation */
static GENC_INLINE void* genc_container_of_notnull_helper(void* obj, ptrdiff_t offset)
{
return ((char*)obj - offset);
}
#if __GNUC__
/* GNU C has some builtin trickery that is almost as good as overloading */
/* function for avoiding multiple evaluation */
static GENC_INLINE const void* genc_container_of_const_helper(const void* obj, ptrdiff_t offset)
{
return (obj ? ((const char*)obj - offset) : NULL);
}
/* function for avoiding multiple evaluation */
static GENC_INLINE const void* genc_container_of_const_notnull_helper(const void* obj, ptrdiff_t offset)
{
return ((const char*)obj - offset);
}
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __GNUC__
#if defined(__cplusplus) || __has_extension(attribute_overloadable)
/* the unused _p attribute is for causing a compiler warning if member_name of
* cont_type does not have same type as target of obj*/
#define genc_container_of(obj, cont_type, member_name) \
({ \
cont_type* _c = ((cont_type*)genc_container_of_helper((obj), offsetof(cont_type, member_name))); \
__typeof__(obj) __attribute__ ((unused)) _p = _c ? &_c->member_name : NULL; \
_c; \
})
#define genc_container_of_notnull(obj, cont_type, member_name) \
({ \
cont_type* _c = ((cont_type*)genc_container_of_notnull_helper((obj), offsetof(cont_type, member_name))); \
__typeof__(obj) __attribute__ ((unused)) _p = &_c->member_name; \
_c; \
})
#else
/* GCC builtin trickery (C mode only, no overloading possible) so that different helpers
* get called for const and non-const types (avoids warnings even with -Wcast-qual).
* Checks if pointers to const type and type are compatible - only the case if
* type is already const - and dispatches to the 2 different helper functions. */
#define genc_container_of(obj, cont_type, member_name) \
({ \
cont_type* _c = \
(cont_type*)(__builtin_choose_expr(__builtin_types_compatible_p(const cont_type*, cont_type*), \
genc_container_of_const_helper, genc_container_of_helper) \
((obj), offsetof(cont_type, member_name))); \
__typeof__(obj) __attribute__ ((unused)) _p = _c ? &_c->member_name : NULL; \
_c; \
})
#define genc_container_of_notnull(obj, cont_type, member_name) \
({ \
cont_type* _c = \
(cont_type*)(__builtin_choose_expr(__builtin_types_compatible_p(const cont_type*, cont_type*), \
genc_container_of_const_notnull_helper, genc_container_of_notnull_helper) \
((obj), offsetof(cont_type, member_name))); \
__typeof__(obj) __attribute__ ((unused)) _p = &_c->member_name; \
_c; \
})
#endif
#else
#define genc_container_of(obj, cont_type, member_name) \
((cont_type*)genc_container_of_helper((obj), offsetof(cont_type, member_name)))
#define genc_container_of_notnull(obj, cont_type, member_name) \
((cont_type*)genc_container_of_notnull_helper((obj), offsetof(cont_type, member_name)))
#endif
static GENC_INLINE void* genc_member_of_helper(void* obj, ptrdiff_t offset)
{
return obj ? ((char*)obj + offset) : NULL;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment