-
Notifications
You must be signed in to change notification settings - Fork 2.5k
[RFC] util: introduce GIT_DOWNCAST macro #4561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I've improved upon the implementation. As this PR is already using compiler-builtins in case |
See 102c52d, which takes care of downcasts in transports and refdb_fs. |
Cool, thanks! I've rebased this PR on master and cherry-picked
your commit.
|
I just found another potential user, so here's a rebased branch: master...tiennou:pks/downcasting. |
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 2424e64 (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.
Thanks, @tiennou, I've adopted your branch. I did make one major change, though: instead of |
@tiennou: want to have another look so that we can get this merged? |
I'm fine with the rename, so unless you need a real look, I should still fine with the rest of the changes 😉. That makes me commutative or something, right ? 🤣 |
You want to approve this PR, then, asOtherwise I'm not allowed to merge? So no, you're not commutative :P |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Damn, my reasoning was wrong ?? I'm fine with those changes, thanks for the cleanup @pks-t !
Thanks to both of you! |
In some parts of our code, we make rather heavy use of downcasting
structures to their respective specialized implementation. One example
is the configuration code with the general
git_config_backend
and thespecialized
diskfile_header
structures. At some occasions, it canget confusing though with regards to the correct inheritance structure,
which led to the recent bug fixed in 2424e64 (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_DOWNCAST
macro, which accepts asparameters 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
parent 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. This is simply done by verifying that the offset of the
member in the target structure is 0.
In case either of the above two conditions is not fulfilled, an
assert
is triggered. Furthermore, these checks do not make use of any
information not known at compile type. As such, the macro should get
optimized away at compile time and have no overhead in case the cast can
be done.
Convert one instance of upcasting to use this new macro as a proof of
concept. This instance is the one that was fixed with 2424e64 --
reverting the change and upcasting to the wrong type does indeed trigger
an assert at runtime. This demonstrates that the error would've been
detected earlier if the new macro had existed back then and was used.