• Show log

    Commit

  • Hash : b5f40441
    Author : Patrick Steinhardt
    Date : 2019-04-16T13:21:03

    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 2424e64c4 (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.