This is unfair to C. With a little work when defining all classes (or non-leaf classes if you're willing for a hairier implementation), you can do this there too.
There are probably other ways, but the way that seems most "obvious" to me is to define the base via union of all bases in the chain, rather than only the immediate base class. So:
/*
class Foo {int f;};
class Bar extends Foo {int b;};
class Qux extends Bar {int q;};
class Leaf extends Qux {int l;};
*/
struct Foo { int f; };
struct Bar { union { struct Foo base, base_Foo}; int b; };
struct Qux { union { struct Bar base, base_Bar; struct Foo base_Foo; }; int q; };
struct Leaf { struct Qux base; int l; }
Then your "compile-time-safe-cast to base class" macro just checks 3 things in order: 1. check if we're already the right class.
2. check if `base` is the right class.
3. unconditionally try to use the appropriately-named `base_Foo` member as calculated from the type.
(runtime-safe-checked downcasts just have to check that the reverse is possible)