I'm creating a button class and am having a hard time deciding between 2 solutions.
1) Templatize the Button class and have it take a function object in its constructor to call when the button is pressed. The guy I'm coding with is worried that this will lead to code bloat/thrashing.
2) Create a ButtonAction base class, and there will be a different ButtonAction for each button. Thus the Button class takes a ButtonAction in its constructor to call when the button is pressed.
We've also considered the use of function pointers, but haven't thought it through that thoroughly.
You could use boost::function<> objects for your actions. This way you don't need any templates and the button class becomes very flexible:
struct Button {
typedef boost::function<void ()> action_t;
action_t action;
Button(const action_t &a_action) : action(a_action) {
}
void click() {
action();
}
};
This way the class is easy to use with function pointers, functor objects or things like boost::bind:
void dosomething();
Button b1 = Button(&dosomething);
struct SomeAction {
void operator()() {}
};
Button b2 = Button(SomeAction());
I'd enumerate the consequences of each alternative, and then decide which option is best for the particular case at hand.
If the template option might (premature worry?) bloat object code, then the polymorphism alternative might make the source code unnecessarily complex.
In the case of templates the compiler will create another Button class for each function object you instantiate it with. This means the product object code will be larger than if you have a single Button class that accepts various action objects through a subclass.
In the case of polymorphism the compiler will generate a single Button class but you will now have another base class to maintain, and you will be forced to subclass it for any new action you add to your collection. In particular you will not be able to use actions that were written before you created the base action class unless you can modify them so they derive from that action class.
The template alternative allows you to use anything at all that conforms to the template's interface. So if you use the template parameter as a function then you can accept anything at all that can be called like a function. This implies you don't even need to consider the alternative of function pointers, since templates allow you to accept function pointers -- and much more.
The polymorphism option implies the compiler knows more about what you're trying to do. In other words, templates come with templates errors.
You can ease some of the template issues if you can find a way to only template Button member-functions, rather than the entire Button class. Take as function parameter an instance of the template so you don't need to explicitly instantiate the template function. Then you win both on some of the template benefits as well as some of the polymorphism benefits.
Related
I often run into the problems associated with SubType Polymorphism, I'm looking for an elegant solution I may not already be aware of.
Here is a simple inheritence hierarchy:
struct BaseClass {
virtual ~BaseClass() = 0;
std::string name;
};
template <T>
struct DerivedClass
{
DerivedClass(const std::string& _name): name(_name) { }
};
Now I might create lots of these DerivedClass instances with different names and template types and store them in an array using their BaseClass.
std::vector<BaseClass*> array;
array.push_back(new DerivedClass<TABC>("abc"));
array.push_back(new DerivedClass<TDEF>("def"));
...
This is pretty standard runtime polymorphism.
However, when I have a new layer of functionality that is type-specific to add and don't want this new layer to be coupled in both directions, I end up having to do something like this:
template <typename T>
void method(DerivedClass<T>* object) { }
void callMethod(BaseClass* object)
{
// this is the logic I'm trying to move up a layer
if (object->name == "abc") method<TABC>(object);
else if (object->name == "def") method<TDEF>(object);
}
Each of these methods has to have the same list of run-time strings to compile-time types to convert, which means adding a new type requires a lot of changes.
If I was to assume the new layer would only support specific options known at compile-time (as is the case here anyway), then it would be feasible to add new types at runtime, but not be able to use them in this layer, which would be fine.
My current thinking is if I was to introduce a virtual method to the class hierarchy that took a function pointer, I could register the function pointers for each method in the second layer based on specific compile-time types (ideally only specified once), kind of like a double dispatch type method.
Any thoughts, suggestions?
You need that link to call the specific template version based on a string, the best you can do is have a dictionary of string->lambda function and use the string as a lookup to get a function<> to call. This avoids the nested ifs and it's relatively easy to maintain, both at compile time (the default list) and at runtime (any changes are just array changes).
Rather than steal Sean Parent's thunder I'll direct you to this talk which will show you how to achieve this cleanly, safely and simply.
The technique is called 'polymorphism as an implementation detail'. It has transformed the way I write code.
https://channel9.msdn.com/Events/GoingNative/2013/Inheritance-Is-The-Base-Class-of-Evil
I'm writing a Window class which propagates different types of events, listed in
enum Event {WINDOW_ClOSE=0x1, WINDOW_REDRAW=0x2, MOUSE_MOVE=0x4, ...};
to objects which have registered for notification with the window. For each type of event, I have an abstract class which any object must extend in order to allow notification. To react to, say, a MOUSE_MOVE event, my object would inherit from MouseMoveListener, which has a process_mouse_move_event() method which is called by Window. Listening to many events can be combined by extending multiple of these classes, which all inherit from the EventListener base class. To register an object, I would call
void Window::register(EventListener* object, int EventTypes)
{
if(EventTypes&WINDOW_CLOSE)
/* dynamic_cast object to WindowCloseListener*, add to internal list of all
WindowCloseListeners if the cast works, else raise error */
if(EventTypes&MOUSE_MOVE)
/* dynamic_cast object to MouseMoveListener*, add to internal list of all
MouseMoveListeners if the cast works, else raise error */
...
}
This works fine, but my gripe is that EventListener is completely empty and that seems code smelly to me. I know I could avoid this by removing EventListener altogether and having a separate Window::register for each type of event, but I feel that this would blow up my interface needlessly (especially since methods other than register might crop up with the same problem). So I guess I am looking for answers that either say:
"You can keep doing it the way you do, because ..." or
"Introduce the separate Window::register methods anyway, because ..." or of course
"You are doing it all wrong, you should ...".
EDIT:
From the link in Igors comment: What I do above only works if there is at least one virtual member in EventListener for example a virtual destructor, so the class is not technically completely empty.
EDIT 2:
I prematurely accepted n.m.'s solution as one of the type "I'm doing it all wrong". However, it is of the second type. Even if I can call EventListener->register(Window&) polymorphically, Window needs to implement a highly redundant interface (in terms of declared methods) that allows EventListeners to register for selective notification. This is equivalent to my alternative solution described above, only with the additional introduction of the EventListener class for no good reason. In conclusion, the canonical answer seems to be:
Don't do dynamic_cast + empty base class just to avoid declaring many similar functions, it will hurt you when maintaining the code later. Write the many functions.
EDIT 3:
I found a solution (using templates) which is satisfactory for me. It does not use an empty base class any more and it does not exhibit the maintenance problem pointed out by n.m.
object->registerWindow (this, EventTypes);
Of course you need to implement registerWindow for all EventListener heirs. Let them check for event types which are relevant to them.
UPDATE
If this means you need to redesign your code, then you need to redesign your code. Why is it so? Because dynamic_cast is not a proper way to do switch-on-types. It is not a proper way because every time you add a class in your hierarchy, you need to go over and possibly update all switches-by-dynamic-cast in your old code. This becomes very messy and unmaintainable very quickly, and this is exactly the reason why virtual functions were invented.
If you do your switch-on-types with virtual functions, every time you change your hierarchy you have to do... nothing. The virtual call mechanism will take care of your changes.
This is what I ended up doing:
template <int EventType> void register_(EventListener<EventType> Listener)
{
// do stuff with Listener, using more templates
};
It turned out that static polymorphism was better suited for my needs - I just wanted to avoid writing
register_mouse_motion_event(...)
register_keyboard_event(...)
and so on. This approach also nicely eliminates the need for an empty base class.
I have a Node class template, which takes a Data type as a template parameter:
template <class T_Data>
class Node
{
};
The Node class is able to notify the user/listener on some events. This functionality is implemented using libsigc++ signals, but before the signal is emitted, the Node notifies a handler object which does some processing and decided whether to emit the signal or not. This handler object exists because in some cases I want node objects to handle their oen events, blocking signals.
The common solution is to give Node virtual methods which anyone can override in a derived class, but since Node uses references to itself and creates objects of its own type, it's easier to have a HandlerBase class and let people derive the handler.
Everything went great until I write a handler class and I wanted my nodes to use it. But in order to enable the new handler, I need to call a static Node method, Node::set_event_handler(). It means I have to remember to call it somewhere. If anyone wants to use my handler, they have to remember to set the handler in main() or in a ctor of some main class, maybe their Window class in a GUI app.
template <class T_Data>
class Node
{
public:
static void set_event_handler (std::unique_ptr <HandlerBase> new_handler);
private:
static std::unique_ptr <HandlerBase> event_handler;
};
So I came up with two possible solutions:
Put the call to set_handler() in some main class I have
Add a T_Handler template parameter to Node class
Currently, the static handler field is set to a new HanderBase, which ignores all signals. If I use the template parameter, it will be possible to have the same Data type with different handlers, and the static field will be set in the initialization, so no extra work will be needed.
The question is, whether the T_Handler not just adds "clutter" to Node, making it less "clean" in the sense of adding a template parameter just for the handler type, which many Node users don't even need.
Actually, I could also give T_Handler a default value, so users can forget about it of they don't need it, but I'm still curious which design is potentially better.
Of course it adds clutter and makes it less clean. However, is it really a big concern? It depends on your perspective, I suppose.
If you look at many of the templates in the standard library, you'll see template parameters that most users don't need. Default values as well as typedef are used to hide this clutter. For example, see std::basic_string, where:
Most users don't care about the traits or Alloc, so there are default values for these parameters
A popular value for charT is simply char, so the std::string type is defined as shorthand.
Note that this clutter is only hidden. It will come out to bite you if you ever need to debug your code and you are looking at the types of your variables in the debugger. ;)
I'm making a Gui Api for games. The user can always use inheritance on the widget and override, but I want callbacks. I want to use a templated callback system:
so if they want to have one for the mouse they inherit from a version of the templated callback base with mouseargs:
So the base would look like this:
template <typename T>
class AguiEventCallback {
public:
virtual void callback(AguiWidget* sender, T arg) = 0;
};
Is it a good idea to mix templates with polymorphism like this? Would I be better off creating callbacks for each of the types I need (mouse, keyboard, gamepad, etc)?
Thanks
Have a look at boost::function and boost::bind. Accept a function object with a defined parameter list for particular events, and callers can do what they want.
This gives callback implementations lots of flexibility and the object generating the events requires even less knowledge of the callback implementation.
For example:
typedef boost::function<void (AguiWidget* sender)> CallbackFunc;
void register_callback(CallbackFunc const& f);
And the client:
class Caller {
void do_register() { register_callback(bind(&Caller::event, this, 123, _1)); }
void event(int arg, AguiWidget* sender) { ... }
};
Just showing function/bind, many other issues ignored; eg. memory management, object lifetime.
Using a template the way you have is fine sometimes. There are issues due to the fact that you must give your template a virtual destructor and that
If you inline your virtual destructor (as you do with most template functions) some compilers find it hard to stick to the One Definition Rule, particularly if the library is used across libraries.
If you do not inline your virtual destructor you have to instantiate every type you are going to use with that template. This is my own preferred approach.
For a callback, you do have the option of using boost::function. This avoids having to derive classes from your template, create them with new and probably stick them into a shared_ptr somewhere. The downside of boost::function as a callback, I have found, is that it is harder to debug into if something goes wrong. Beware of that issue.
Momentarily accepting your virtual dispatch solution, what your templated approach guarantees is a uniformity in the callback function name and arguments. Sadly, that will force a lot of other code to disambiguate which callback is being invoked / overridden, probably causing more trouble than good.
That said, as janm said other options exist. Functors are more powerful (you can change them on an existing object at run-time, you can have lists of observers) but also have to be initialised at the right time (pure virtual functions effectively remind the programmer to supply them at compile time), and introduce a bigger variety of run-time states to reason about and understand.
You might also be able to use a template policy or the Curiously Recurring Template Pattern to supply behaviours at compile time, allowing inlining, dead code elimination, type-specific behaviours and other optimisations.
In addition to other answers here you might have a look to the boost::signal library. It implements a signal/slot mechanism which is indeed useful for GUIs. Performance is not as good as you would expect (costs more than a call to a virtual method) but for a GUI it's just fine.
The boost::signal library can also be used together with boost::bind and this combo is very powerful.
I don't like using inheritance very much for callbacks. It spawns a lot of classes with just 1 method most of the time. It's C++ not Java. you have functions, use them :)
I'm trying to make a button class (abstract) so I can set what function is that button going to trigger when clicked dynamically when my program load.
I want to construct all my buttons by reading XML files, this is to avoid code replication so having only 1 "generic" button class is really useful for me.
I was wondering if you could dynamically pass the necessary information about a method, like a pointer to the method's owner and method in question name, or even better the direct pointer to the method, for a button to call that function/method when clicked ?
Since pointer to function is a runtime artifact you cannot store that in the offline configuration. I see two solutions that might fit what you describe:
put your functions into a dynamic library and load them by name - that way your configuration would map a button to library path/function name pair,
build a "registry" of named function pointers at startup, probably some hash table, so the configuration would map a button to the hash key.
From experience though I would say that building such facilities are usually overkill, and the configuration quickly becomes heavier then the app itself.
Some additional pointers: Boost.Signals, QT Signals, Command and Chain of Responsibility design patterns.
You can create a SetFunctor method in your generic Button class, which accepts a function object as a parameter. Then you create a Call method that calls the function wrapped inside the function object. Something like this:
template<typename FUNC>
class Functor
{
typedef FUNC (*FC)();
FC func;
public:
Functor( FC f ) : func(f) {}
~Functor() {}
FUNC Call() { return func(); }
FUNC operator()() const { return Call(); }
};
class Button
{
std::auto_ptr<Functor<void> > pfunc;
public:
Button() {}
~Button() {}
void SetFunctor( void(*fc)() )
{
pfunc.reset( new Functor<void>( fc ) ); // now owns ptr;
}
void Call()
{
pfunc->Call();
}
};
...
void changeColor()
{
// do work
}
Button obj;
obj.SetFunctor( changeColor );
obj.Call();
Of course I could've used better smart pointers and or better techniques, but this should give you a gist of what I was hinting at. Also note, that this Functor object can only accept functions that have no parameters. You can change this to your liking. I hope it helps!
UPDATE: A few fixes added. Sorry about that!
You are looking for the signal/slot pattern.
In C++ two popular options are boost signals and sigc.
There are a couple of ways to link a button to a function in C++ - you can use function pointers (which are somewhat scary), or a signals and slots pattern which provides the functionality of function pointer with more type safety.
As far as wiring up the function pointers or signals and slots at run time based on a config file, to do this elegantly will require reflection, which C++ doesn't provide for you. I suspect this will be the ugly part.