Commit b5f40441 by Patrick Steinhardt

util: introduce GIT_CONTAINER_OF macro

In some parts of our code, we make rather heavy use of casting
structures to their respective specialized implementation. One
example is the configuration code with the general
`git_config_backend` and the specialized `diskfile_header`
structures. At some occasions, it can get confusing though with
regards to the correct inheritance structure, which led to the
recent bug fixed in 2424e64c (config: harden our use of the
backend objects a bit, 2018-02-28).

Object-oriented programming in C is hard, but we can at least try
to have some checks when it comes to casting around stuff. Thus,
this commit introduces a `GIT_CONTAINER_OF` macro, which accepts
as parameters the pointer that is to be casted, the pointer it
should be cast to as well as the member inside of the target
structure that is the containing structure. This macro then tries
hard to detect mis-casts:

- It checks whether the source and target pointers are of the
  same type. This requires support by the compiler, as it makes
  use of the builtin `__builtin_types_compatible_p`.

- It checks whether the embedded member of the target structure
  is the first member. In order to make this a compile-time
  constant, the compiler-provided `__builtin_offsetof` is being
  used for this.

- It ties these two checks together by the compiler-builtin
  `__builtin_choose_expr`. Based on whether the previous two
  checks evaluate to `true`, the compiler will either compile in
  the correct cast, or it will output `(void)0`. The second case
  results in a compiler error, resulting in a compile-time check
  for wrong casts.

The only downside to this is that it relies heavily on
compiler-specific extensions. As both GCC and Clang support these
features, only define this macro like explained above in case
`__GNUC__` is set (Clang also defines `__GNUC__`). If the
compiler is not Clang or GCC, just go with a simple cast without
any additional checks.
parent ed959ca2
...@@ -30,6 +30,17 @@ ...@@ -30,6 +30,17 @@
# define max(a,b) ((a) > (b) ? (a) : (b)) # define max(a,b) ((a) > (b) ? (a) : (b))
#endif #endif
#if defined(__GNUC__)
# define GIT_CONTAINER_OF(ptr, type, member) \
__builtin_choose_expr( \
__builtin_offsetof(type, member) == 0 && \
__builtin_types_compatible_p(typeof(&((type *) 0)->member), typeof(ptr)), \
((type *) (ptr)), \
(void)0)
#else
# define GIT_CONTAINER_OF(ptr, type, member) (type *)(ptr)
#endif
#define GIT_DATE_RFC2822_SZ 32 #define GIT_DATE_RFC2822_SZ 32
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment