[engine] Howto check if a method is overridden in C++?
Posted: 18 May 2014, 22:38
Usage:
Some time ago I wanted to simplify EventHandler & EventClient and added an auto-linker for the used events. So that it automatically links e.g. DrawScreen() if it is overridden by the derived class of CEventClient to safe nasty code of the following type:
I always disliked the string comparisons, esp. when you got many events linked, the code becomes confusing and error-prune.
Problem:
So how do you now detect if Foo overrides i.e. DrawScreen?
You will quickly find out that it isn't easy at all and yeah you will even get a feeling that something like that would be nasty in c++.
Your first naive idea will likely be:
Too bad, it will fail. Cause you don't retrieve pointers to the actual functions there, instead you get a PMF (pointer to member functions) that just says what name it should lookup in the vtable. And so it will always return false (the `pointers` are equal).
Then you might think there will be a way to retrieve the actual function pointer. There is something similar:
Here you resolve the pointer and cast it to an actual `void(*)()`, the problem is that member functions aren't regular functions and depending on the compiler something like that can be impossible (you could never call such a function pointer!).
So this case is explicitly illegal in C++. You cannot cast a PMF to a PF!
Still GCC seems to implement PMFs in a way that they can offer you this possibility via a custom PMF extension (-Wno-pmf-conversions).
This is what I used in the first place and worked fine so far. But then abma & others wanted to have llvm support at least as dev enviroment (not for syncing online matches). So there was a llvm buildbot added. First, a lot of other bugs needed to be fixed then. But then only this one was left: LLVM doesn't got such a PMF extension and nothing even similar.
After I have realized that there was no way to get that code working at all (casting PMFs is a no-go in c++!) and that my code was nasty itself. I was advised to remove the check and to just autolink all events even those that aren't requested.
But I didn't wanted to give up such early. I thought there _must_ be a way.
And there is :D
The Holy Grail (Solution):
It seems very simple at first, and not much different from the first naive solution. But it is.
Just lets check what typeid() returns:
What do we see?
Links:
Stackoverflow: http://stackoverflow.com/a/23725880/3650440
ideone.com: http://ideone.com/xcQOT6
Some time ago I wanted to simplify EventHandler & EventClient and added an auto-linker for the used events. So that it automatically links e.g. DrawScreen() if it is overridden by the derived class of CEventClient to safe nasty code of the following type:
Code: Select all
class Foo: public CEventClient {
...
bool WantsEvent(const std::string& eventName) {
return (eventName == "DrawScreen") || (eventName == "DrawWorld");
}
void DrawScreen();
void DrawWorld();
...
};
Code: Select all
class Foo: public CEventClient {
...
void DrawScreen();
void DrawWorld();
...
Foo() : CEventClient(...) {
autoLinkEvents = true;
RegisterLinkedEvents(this); // auto-linking happens here
eventHandler.AddClient(this);
}
};
Problem:
So how do you now detect if Foo overrides i.e. DrawScreen?
You will quickly find out that it isn't easy at all and yeah you will even get a feeling that something like that would be nasty in c++.
Your first naive idea will likely be:
Code: Select all
bool IsFooOverridden() {
return !(&Derived::Foo == &Base::Foo);
}
Then you might think there will be a way to retrieve the actual function pointer. There is something similar:
Code: Select all
bool IsFooOverridden() {
return !(reinterpret_cast<void(*)()>(&Derived::Foo) == reinterpret_cast<void(*)()>(&Base::Foo));
}
So this case is explicitly illegal in C++. You cannot cast a PMF to a PF!
Still GCC seems to implement PMFs in a way that they can offer you this possibility via a custom PMF extension (-Wno-pmf-conversions).
This is what I used in the first place and worked fine so far. But then abma & others wanted to have llvm support at least as dev enviroment (not for syncing online matches). So there was a llvm buildbot added. First, a lot of other bugs needed to be fixed then. But then only this one was left: LLVM doesn't got such a PMF extension and nothing even similar.
After I have realized that there was no way to get that code working at all (casting PMFs is a no-go in c++!) and that my code was nasty itself. I was advised to remove the check and to just autolink all events even those that aren't requested.
But I didn't wanted to give up such early. I thought there _must_ be a way.
And there is :D
The Holy Grail (Solution):
Code: Select all
bool IsFooOverridden() {
return !(typeid(&Derived::Foo) == typeid(&Base::Foo));
}
Just lets check what typeid() returns:
Code: Select all
struct A { virtual void Foo() {} };
struct B : public A { void Foo() {} void Bar() {} };
struct C : public A { };
int main() {
typeid(&A::Foo).name() -> "M1AFvvE"
typeid(&B::Foo).name() -> "M1BFvvE"
typeid(&C::Foo).name() -> "M1AFvvE" <- !!!
typeid(&B::Bar).name() -> "M1BFvvE"
}
- The typeid only depends on the class where it is defined in and on the arguments of the method, it doesn't depend on the method's name (B::Foo & B::Bar return same type!), so we can't use it to compare 2 different events with equal arguments.
- In case the function isn't overriden it returns the typeid of the class where the method actually is defined in!!! C::Foo returns the same typeid as for A::Foo!!!
Links:
Stackoverflow: http://stackoverflow.com/a/23725880/3650440
ideone.com: http://ideone.com/xcQOT6