[engine] Howto check if a method is overridden in C++?

[engine] Howto check if a method is overridden in C++?

Happenin' news on what is happening in the community. Content releases, new tutorials, other cool stuff.
Post Reply
User avatar
jK
Spring Developer
Posts: 2299
Joined: 28 Jun 2007, 07:30

[engine] Howto check if a method is overridden in C++?

Post by jK »

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:

Code: Select all

class Foo: public CEventClient {
...
	bool WantsEvent(const std::string& eventName) {
		return (eventName == "DrawScreen") || (eventName == "DrawWorld");
	}

	void DrawScreen();
	void DrawWorld();
...
};
I always disliked the string comparisons, esp. when you got many events linked, the code becomes confusing and error-prune.

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);
}
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:

Code: Select all

bool IsFooOverridden() {
	return !(reinterpret_cast<void(*)()>(&Derived::Foo) == reinterpret_cast<void(*)()>(&Base::Foo));
}
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):

Code: Select all

bool IsFooOverridden() {
	return !(typeid(&Derived::Foo) == typeid(&Base::Foo));
}
It seems very simple at first, and not much different from the first naive solution. But it is.
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"
}
What do we see?
  • 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!!!
And that's what we wanted! Now we can check if a method is overridden or not :D

Links:
Stackoverflow: http://stackoverflow.com/a/23725880/3650440
ideone.com: http://ideone.com/xcQOT6
User avatar
Silentwings
Posts: 3720
Joined: 25 Oct 2008, 00:23

Re: Blog: How to check if a virtual method is overridden in

Post by Silentwings »

Nice! Had wondered about that before.
Post Reply

Return to “Community Blog”