Created
June 17, 2012 15:46
-
-
Save pmj/2944912 to your computer and use it in GitHub Desktop.
Super safe container_of macro for structs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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