C++: Can you overload functions between base/sub classes?

Started by
8 comments, last by Gamer Gamester 16 years, 2 months ago
I haven't been able to find info on this specific question. So here it is: Suppose you have a two classes, class A, and class B which is a descendant of A. Can you overload a function based on references to these? For example: void Function( A& a ); void Function( B& b ); My guess is no, as if you pass a B& in, both functions will match. But I thought it might be possible as perhaps C++ defaults to the more specific of the two. Anybody know for sure? And if it's not possible, anybody have a suggestion for an alternate way to perform similar functionality? Thanks
Advertisement
Compiler will choose closer match.

Just be aware that sometimes the results might be unexpected. It gets especially nasty with const modifiers.

template < class T >void foo( const T & t ); // 1)void foo( B &b );        // 2)foo( B("SomeB") );  // picks 1)B b("AnotherB");foo( b );           // should pick 2)


There might be hidden pitfalls to this, but such specific resolution is possible.
It'll pick the closest match.
Couldn't give you an alternative way to perform the same functionality without knowing what those functions and classes actually do.
If it picks the closest match then I should be okay, and might not need an alternative way. But for the chance that an alternative is better, here's what I'm trying to do:

I have two classes, an abstract interface class and a descendant implementation class.
class Interface{};class Implementation{} : public Interface;

Interface, among other things, will be passed objects that descend from base class Event.
class Event{};

I want to be able to add different types of Events later, all descending from class Event, with only having to change Implementation. (I don't want to have to change Interface, as then everything that uses Interface needs to recompile/change)

And so as far as Interface is concerned, it has a pure virtual function that takes a reference to Event.
Interface::Register( Event& event );

Implementation will then distinguish between the different types of Event descendants, processing them accordingly. I would prefer to do this through function overloading, as I find it more pleasing than casts.
Implementation::Register( Event& event );Implementation::Register( KeyEvent& event );Implementation::Register( MouseEvent& event );etc....

However, as I'm thinking about this... I'm finding a new concern. Since the class I'm calling Register() on may be a Interface& to an Implementation, will the "closest match" get picked properly even though this closest match only exists in Implementation, and not in Interface? Or will it automatically pick Register( Event& event ) because that is the only one that actually exists in Interface?

Or is my entire approach silly and inferior to some un-thought-of (by me) approach?

Thanks!
easily tested

class Event{};class MouseEvent : public Event{};class KeyEvent : public Event{};class NoEvent : public Event{};class EventManager{public:	void OnEvent(Event& e)	{		std::cout << "unknown event class" << std::endl;	}	void OnEvent(MouseEvent& e)	{		std::cout << "mouse event class" << std::endl;	}	void OnEvent(KeyEvent& e)	{		std::cout << "key event class" << std::endl;	}};int main(){	NoEvent ne;	MouseEvent me;	KeyEvent ke;	Event& e1 = ne;	Event& e2 = me;	Event& e3 = ke;	EventManager e;	e.OnEvent(ne);	e.OnEvent(me);	e.OnEvent(ke);	e.OnEvent(e1);	e.OnEvent(e2);	e.OnEvent(e3);	return 0;}


outputs (vc2005):
unknown event classmouse event classkey event classunknown event classunknown event classunknown event class


so unfortunately it won't do what you want.

instead of relying on function-overloading (compile time), couldn't you use a virtual function on event (run-time)

e.g.
class Event{public:	 virtual void OnRegister() = 0;};class MouseEvent : public Event{public:	 virtual void OnRegister()	 {		 std::cout << "registered mouse event" << std::endl;	 }};class KeyEvent : public Event{public:	 virtual void OnRegister()	 {		 std::cout << "registered key event" << std::endl;	 }};class NoEvent : public Event{public:	 virtual void OnRegister()	 {		 std::cout << "registered no event" << std::endl;	 }};class EventManager{public:	void Register(Event& e)	{		e.OnRegister();	}};int main(){	NoEvent ne;	MouseEvent me;	KeyEvent ke;	Event& e1 = ne;	Event& e2 = me;	Event& e3 = ke;	EventManager e;	e.Register(ne);	e.Register(me);	e.Register(ke);	e.Register(e1);	e.Register(e2);	e.Register(e3);	return 0;}


that way, you can add as many new Event types as you like with out changing the
EventManager.

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Quote:I have two classes, an abstract interface class and a descendant implementation class.


Decide what you want to use - compile-time or run-time polymorphism.

If you're using compile-time polymorphism, you don't need any kind of inheritance or interfaces. Just concrete classes. Everything else is resolved during compile time.

Run-time polymorphism is a different beast. There, everything works through pointers and virtual function calls.

Why do you need interface abstraction for events anyway? These are used to abstract functionality, I can't think of a way where this would be useful with events.
I want an interface abstraction for my events so that I can easily change the low-level implementation of them without affecting the high-level use of them.

It's based on the Dependency Inversion Principle: "High level modules should not depend upon low level modules. Both should depend upon abstractions."

Let's say I'm using some library to read events. This is the low-level (from the point of view of my program) functionality of event handling.

Then I have the actual events, such as: When the 'M' key is pressed do List of stuff. This is the high-level functionality.

Now suppose a month from now I want to change my event-reading library. (From SDL to GLFW, for example) Now instead of changing code for every high-level use of Events (for every different Event that occurs!), I can instead change it in one place, the low-level Event Implementation. Since every class that used an event depended upon the Event Interface, not the Implementation, these high-level classes can remain untouched.

What happens when you press an 'M' key remains the same, regardless of how I go about detecting that 'M' key press.

I hope that makes sense.
You seem to be trying to implement the visitor pattern in C++. You may want to google "C++ visitor". In particular, you may want to read this article and the visitor section of the book "Modern C++ Design".
Quote:Original post by BeauMN
Let's say I'm using some library to read events. This is the low-level (from the point of view of my program) functionality of event handling.


What's wrong with boost::signal or any of its many counterparts? These libraries provide event system. That by itself is the abstraction that de-couples the systems.

And since systems were de-coupled, you also lose the notion of "low" and "high" level functionality.

It just sounds to me you're abstracting the wrong thing. Event system is concrete. Event system *is* the abstraction. Its implementation isn't.

But producers and consumers/publishers and subscribers/events and observers are implementation dependent, yet de-coupled through event system.

This is why I'm having hard time understanding why it makes no sense for events to be interfaces. If you design them in such a way, you're going to tightly couple them to SDL's event design, and lose all chances of portability.
So I ran some test code and it had the exact problem I was concerned about. Calling Register() on an Interface& to the Implementation always treats the parameter as an Event, while calling it on the Implementation directly will find the "closest match".

SiCrane:
Thanks for informing me of the Visitor Pattern. I read about it and believe I can use this to solve my problem. I'm off to try it.

Antheus:
I've never looked into boost:signal, so no I don't see anything wrong with it. [grin]

Maybe we're thinking about about different things, but I still think my design makes sense. A little more to help clarify what I'm doing (or to help you clarify to me what may be wrong [wink])

My Event Interface is very minimal and abstract, and I don't believe tied in any way to my underlying event library's design (And to insure this, I'm designing the interface first, before I actually implement it with the library. I plan to use GLFW to later do this implementation)

The Interface simply has you register an event, such as pressing a key, moving a joystick, closing a window. Then, when this event occurs, the callback function of the event is called. From the point of view of anything that uses the Interface (the high-level logic of my entire program), this is all that happens.

This Interface could be implemented by a variety of underlying libraries. And even multiple ways with the same library. (Do I check certain events every frame? Every 10 seconds? Differently for different event types? Do I want to change the priority of event types? Enable/Disable event types? etc...)

Perhaps it makes more sense to call my Implementation a Wrapper, as in it wraps the events from my system (Interface) to whatever other system (GLFW, SDL, etc). But I like to call it Implementation because it's as low-level as my code will get (The underlying library digging lower).

I like to wrap all external libraries in such a fashion (I classify them all as low-level). Then I end up with a set of interfaces/modules that provide precisely the functionality I want, in the way that I want, and I write all my high-level code using these.

This topic is closed to new replies.

Advertisement