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