I'm currently working on designing a base for future projects, more specifically I'm working on the input handling. I'm using the command pattern for handling the input, when creating an input context the programmer can bind a command to a key or mouse button through an invoker that executes the command depending on the different conditions of the application, key pressed, where the mouse is on the window and so on.
I ran into trouble when I got to the part of adding handling of the mouse when in an input context where the cursor is disabled, e.g. when controlling a 3D camera (This is actually the only situation I can think of where this would be useful).
The way I see this working, is the programmer binds a command, one that rotates the camera, to be activeated once an event is created that describes mouse movement. The command would hold a pointer to the camera object and call a function like camera->pan() when executed. This command would be executed when the mouse moved in the X-Axis. However if this was the case, the camera would always pan with a constant speed, no matter how fast or slow the mouse was moved. If the cameras function pan() had a parameter for specifying how much to pan, the Command object would need to have a value for this parameter when executing. If that value is specified on creation of the command and stored as a member, the problem would arise again, since the parameter would have the same value every time the function is called.
My proposed solution to this problem is to simply create a variant of the Command class called something like WeightedCommand that had a parameter in its execute() function. This parameter would be a "weight" passed on to the cameras pan() function. This would allow for the command to be executed with a differen "weight" everytime it's called, or the same "weight", it would be up to the programmer to decide.
For reference, this is an example of the Command pattern, from wikipedia.
class Light {
public:
void TurnOn() { std::cout << "The light is on." << std::endl; }
void TurnOff() { std::cout << "The light is off." << std::endl; }
};
class ICommand {
public:
virtual ~ICommand() = default;
virtual void Execute() = 0;
};
// The Command for turning on the light - ConcreteCommand #1
class FlipUpCommand : public ICommand {
public:
FlipUpCommand(Light* light) : light_(light) { assert(light_); }
void Execute() { light_->TurnOn(); }
private:
Light* light_;
};
An example of the WeightedCommand:
class WeightedCommand
{
public:
virtual ~WeightedCommand() = default;
virtual void execute(double weight) = 0;
};
class PanCamera : public WeightedCommand
{
public:
PanCamer(Camera* cam)
: _camera(cam;
{}
void execute(double weight)
{
_camera->pan(weight);
}
private:
Camera* _camera;
};
Can you see any flaws with this approach. Is there a better solution already available? I tried searching for solutions to similar problems, but couldn't find anything that really fit.
This is rather an opinion based question but here are some suggestions.
You could keep your approach and generalize it a bit more by making ICommand a template.
template <typename ...Args>
class ICommand
{
public:
virtual ~ICommand() = default;
virtual void Execute(Args const& ...args) = 0;
};
class PanCamera : public ICommand<double>
{
void Execute(double const& pan) override
{
_camera->pan(pan);
}
};
If you want to store your commands in a container you need a common type, which wouldn't work with the above example. To avoid this you can replace the double parameter with a std::variant like you've already mentioned.
using CommandArgs = std::variant<double, std::string>;
class PanCamera : public ICommand<CommandArgs>
{
void Execute(CommandArgs const& args) override
{
_camera->pan(std::get<double>(args));
}
};
class SayHello : public ICommand<CommandArgs>
{
void Execute(CommandArgs const& args) override
{
display->sayHello(std::get<std::string>(args));
}
};
You can also ditch your ICommand interface alltogether and use the visitor pattern for std::variant.
struct PanCameraArgs
{
double value = 0;
};
struct SayHelloArgs
{
std::string text;
};
struct RotateCameraArgs
{
double angle = 0;
};
using CommandArgs = std::variant<PanCameraArgs, SayHelloArgs, RotateCameraArgs>;
void dispatchCommand(CommandArgs const& command)
{
std::visit( overloaded {
[&] (PanCameraArgs const& args)
{
_camera->pan(pan.value);
}
[&] (SayHelloArgs const& args)
{
display->sayHello(args.text);
}
[&] (RotateCameraArgs const& args)
{
_camera->rotate(args.angle);
}
}, command);
}
Related
I'm trying to learn decorator design and I came up with something awesome, but I don't know if my idea will compile. So I created some classes:
this is the base class
class parameter
{
public:
parameter(){}
parameter(double mini, double maxi, double def) :
mini(mini),
maxi(maxi),
def(def)
{}
double mini, maxi, def;
double val;
virtual double getValue() { return val; }
virtual void setValue(double v) { val = v; }
};
This class stores smoothedParameters. smoothedParameter will add itself to the SmootherManager when they need to be smoothed and remove themselves when they are finished.
class SmootherManager
{
public:
SmootherManager() {}
juce::Array<smoothedParameter *> CurSmoothingList;
void add(smoothedParameter * sp)
{
CurSmoothingList.addIfNotAlreadyThere(sp);
}
void remove(smoothedParameter * sp)
{
CurSmoothingList.removeFirstMatchingValue(sp);
}
void doSmoothing()
{
for (auto & sp : CurSmoothingList)
sp->incValue();
}
};
This class takes values over time and outputs a smoothed value.
class smoothedParameter : public parameter
{
public:
//smoothedParameter(){}
smoothedParameter(double smoothingSpeed, SmootherManager & manager, parameter * p) :
smoothingSpeed(smoothingSpeed),
manager(manager),
p(p)
{}
double smoothingSpeed;
SmootherManager & manager;
parameter * p;
rosic::ExponentialSmoother smoother;
double getValue()
{
return smoother.getCurrentValue();
}
void setValue(double v)
{
p->setValue(v);
smoother.setTargetValue(p->getValue());
if (!smoother.finishedSmoothing())
manager.add(this);
}
void incValue()
{
smoother.getSample();
if (smoother.finishedSmoothing())
manager.remove(this);
}
};
This class takes a value and modifies it over time via a list of modifiers.
class modulatedParameter : public parameter
{
public:
modulatedParameter(parameter * p) : p(p) {}
juce::Array<modifier *> modulationInputs;
parameter * p;
double getValue()
{
double totalMod = 0;
for (const auto & m : modulationInputs)
totalMod += m->val;
return totalMod * p->getValue();
}
void setValue(double v)
{
p->setValue(v);
}
void add(modifier * sp)
{
modulationInputs.addIfNotAlreadyThere(sp);
}
void remove(modifier * sp)
{
modulationInputs.removeFirstMatchingValue(sp);
}
};
So here's how it works. You have a smoother and a modulator. If you construct a smoother inside the modulator, you get a smoothed modulator. If you construct a modulator inside a smoother, you get a non-smoothed modulator.
Here's how I wanted to use the classes:
// create the smoother manager
SmootherManager smManager;
// create modulatable parameter
auto mp = new modulatedParameter(new parameter(0.0, 1.0, 0.0));
// create a smoothable parameter
auto sp = new smoothedParameter(0.01, smManager, new parameter(0.0, 1.0, 0.0));
// create a modulatable parameter where its modifiers are smoothed
auto mp_sp = new modulatedParameter(new smoothedParameter(0.01, smManager, new parameter(0.0, 1.0, 0.0)));
// create a parameter where values are smoothed, but the modulation is not
auto sp_mp = new smoothedParameter(0.01, smManager, modulatedParameter(new parameter(0.0, 1.0, 0.0)));
ok! here's problem.
modifier myMod;
// add a modifier to sp_mp, can't do it, sp_mp has no add function.
sp_mp->add(&myMod);
I'm trying to add a modulator to the modulatedParameter of smoothedParameter. I thought of a way, but this seems wrong.
auto mp = new modulatedParameter(sp_mp->p);
mp->add(&myMod)
sp_mp = new smoothedParameter(0.01, smManager, mp));
Any time I want to add/remove a modifier, I have to go through several steps. I could think of a way to remedy this but I am just so lost as to what is a practical approach because I don't know all the possibilities of C++. The point of decorator design is that objects can have a different set of functions. ...It seems like I'd need to have an "add/remove" function for every class, defeating the purpose of this design.
The point of decorator design is that objects can have a different set
of functions.
No, the point of decorator is to get the ability of flexibly extending the object`s base functionality, while preserving its core. Usually, the word "flexibly" presumes making this extension at run-time (dynamically).
Meanwhile, C++ is statically-typed language. It means that the type of an object/variable defines, what you are allowed to do to it and what you are not. sp_mp->add(&myMod); possible IIF the type (class) of the variable sp_mp has add(...) function. This decision is made at compile-time and no design pattern can change this fact, just bare with it. C++ compiler won't let you call functions/use member variables of the variable which are not part of its type.
No matter what you do, the interface of existing type is defined statically. Wanna change it? Do it at compile-time.
Now, taking into account everything was said, we can make a logical conclusion:
If you want to add some new functions to an existing type - create a new type.
Here is a more or less classic (I believe) Decorator implementation. *I did not used shared pointers just because... OP did not use them either :)
class ICore
{
public:
virtual std::string Description() = 0;
void Describe() {
std::cout << "I am " << Description() << std::endl;
}
};
class Core final : public ICore
{
public:
std::string Description() override {
return "Core";
}
};
class IDecorator : public ICore
{
protected:
ICore* core;
public:
IDecorator(ICore* _core)
: core{ _core }
{ }
virtual ~IDecorator() {
delete core;
}
};
class Beautiful final : public IDecorator
{
public:
Beautiful(ICore* _core)
: IDecorator{ _core }
{ }
public:
std::string Description() override {
return "Beautiful " + core->Description();
}
};
class Shiny final : public IDecorator
{
public:
Shiny(ICore* _core)
: IDecorator{ _core }
{ }
public:
std::string Description() override {
return "Shiny " + core->Description();
}
};
int main()
{
ICore* core = new Core;
ICore* decorated_core = new Beautiful{ new Shiny{ core } };
core->Describe();
decorated_core->Describe();
delete decorated_core;
return 0;
}
Output:
I am Core
I am beautiful shiny Core
As you see, here Decorator did not change an interface (class prototype) - no new functions were added to the core. Also, it did not change any existing functionality. What it did, however, was the extension of the already existing behavior. It literally decorated the description of the core with 2 new word. And note - this decoration happened at runtime. If we decided to change the decoration order from new Beautiful{new Shiny{core}} to new Shiny{new Beautiful{core}} the word order would change too (from beautiful shiny Core to shiny beautiful Core).
However, if you really-really want to fulfil your primary intent - adding a brand new function with decorator... There is a way, which lets you imitate such behavior. It would look ugly in C++14 so here is a C++17 code:
class Core
{
public:
void CoreFunctional() {
std::cout << "Core functional." << std::endl;
}
};
template<typename T>
class Extend : public virtual T
{
public:
Extend() = default;
Extend(const T&) { }
public:
void ExtendedFunctional() {
std::cout << "Extended functional." << std::endl;
}
};
template<typename T>
class Utility : public virtual T
{
public:
Utility() = default;
Utility(const T&) { }
public:
void UtilityFunctional() {
std::cout << "Utility functional." << std::endl;
}
};
int main()
{
Core core;
core.CoreFunctional();
auto decorated_core = Utility{Extend{core}};
decorated_core.CoreFunctional();
decorated_core.ExtendedFunctional();
decorated_core.UtilityFunctional();
}
The output is just as you would expect, but I am not really sure, if that may be considered to be a decorator...
The point of decorator design is that objects can have a different set of functions. ...It seems like I'd need to have an "add/remove" function for every class, defeating the purpose of this design.
No. Decorator pattern, as almost all the most known patterns, is all about interfaces and thus (in C++) virtual member functions.
You define your base class (either an abstract one or a concrete one you want to use as a base) where methods that can be decorated are virtual.
A decorator decores something that exists, it neither adds nor removes functions.
Whenever you define a decorator, you end up overriding those methods to enrich them and iteratively call the base class implementation of the same method. Then you pass around pointers/references to the base class and the user doesn't know if they are decorated or not. Just call it and the right thing will happen.
Let's consider this. If you add a new method, how could you invoke it from a reference or a pointer to the base class? You cannot, so you need the actual type, that is the derived one.
This defeats the purpose of the design, not the fact that you must add a method to a base class to be able to decorate it in a derived one.
If you are looking for a pattern that lets you add or remove functions from a class, consider mixins or whatever. That's not the goal of the decorator.
I'm coding a game engine and I have this class set up for objects:
class SceneManager //controls everything in the "world" game
{
public:
void Add(SceneObject* object); //adds to the vector
private:
vector<SceneObject*> _worldObjects; //the vector that contains all of them
}
And all classes I work on the game inherit from SceneObject:
class SceneObject
{
public:
virtual void Draw() = 0;
}
class Image : public SceneObject
{ }
class Sprite : public SceneObject
{ }
class Model3D : public SceneObject
{ }
So I know I can call Draw() for all objects in my vector.
But I've been working on optimizations and I'm trying to get rid of all inheritance and virtual functions, and use composition instead, since they can't be inlined and seems to be a major performance issue when performed on a per-object basis.
I'm looking for some C++ technique that I can use to be able to store a bunch of SceneObjects in my vector, and then call Draw() on it and it properly draws the object related to it. This will also work for the Update() function I'm using as virtual.
So this code:
void SceneManager::Add(SceneObject* object)
{
_worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
for(unsigned int i = 0; i < _worldObjects.size(); i++)
{
_worldObjects[i]->Draw(); //SceneObject's being called
}
}
...would become:
void SceneManager::Add(Image* image)
{
SceneObject* object = new SceneObject();
//link object to image somehow, tried to use it as a member of image
_worldObjects.push_back(object);
}
void SceneManager::DrawTheWorld()
{
for(unsigned int i = 0; i < _worldObjects.size(); i++)
{
//_worldObjects[i]->
//I need somehow to be able to get the pointer back to the original class
//It can be an image, sprite, model3d, anything
}
}
I don't think if I add a switch or if/elses and removing the virtual I'd gain any performance, so I'm trying to figure if there's a clean way to deal with this.
Any ideas?
You can use free functions to model the drawable aspect of your objects:
#include <iostream>
class Image { };
class Sprite { };
class Model3D { };
namespace draw_aspect
{
void draw(Image const& image) { std::cout << "drawing image\n"; }
void draw(Sprite const& sprite) { std::cout << "drawing sprite\n"; }
void draw(Model3D const& model3D) { std::cout << "drawing model3D\n"; }
}
Now, either use three separate vectors (this could well be most optimal, depending on the ordering relationship between the objects across collections?), or consider a variant type vector:
1. Using variant types
#include <boost/variant.hpp>
using SceneObject = boost::variant<Image, Sprite, Model3D>;
namespace draw_aspect {
struct draw_visitor : boost::static_visitor<> {
template <typename T> void operator()(T const& t) const { draw(t); }
};
void draw(SceneObject const& sobj) {
static const draw_visitor _vis;
boost::apply_visitor(_vis, sobj);
}
}
A complete proof of concept of the latter: Live on Coliru
#include <vector>
class SceneManager //controls everything in the "world" game
{
public:
void Add(SceneObject v) { _worldObjects.emplace_back(std::move(v)); }
friend void draw(SceneManager const& sm) { return sm.draw(); }
private:
void draw() const {
for(auto& sobj : _worldObjects)
draw_aspect::draw(sobj);
}
std::vector<SceneObject> _worldObjects; //the vector that contains all of them
};
int main()
{
SceneManager sman;
sman.Add(Image());
sman.Add(Sprite());
sman.Add(Model3D());
sman.Add(Image());
draw(sman);
}
Outputs
drawing image
drawing sprite
drawing model3D
drawing image
2. Separate collections
The alternative using separate vectors: Live on Coliru
class SceneManager //controls everything in the "world" game
{
public:
void Add(Image v) { _images .emplace_back(std::move(v)); }
void Add(Sprite v) { _sprites .emplace_back(std::move(v)); }
void Add(Model3D v) { _model3Ds.emplace_back(std::move(v)); }
friend void draw(SceneManager const& sm) { return sm.draw(); }
private:
void draw() const {
for(auto& sobj : _images) draw_aspect::draw(sobj);
for(auto& sobj : _sprites) draw_aspect::draw(sobj);
for(auto& sobj : _model3Ds) draw_aspect::draw(sobj);
}
std::vector<Image> _images;
std::vector<Sprite> _sprites;
std::vector<Model3D> _model3Ds;
};
int main()
{
SceneManager sman;
sman.Add(Image());
sman.Add(Sprite());
sman.Add(Model3D());
sman.Add(Image());
draw(sman);
}
Note that the output is different (ordering):
drawing image
drawing image
drawing sprite
drawing model3D
Solving your specific petition is one thing that others have already done.
However, I think you should take a step back and consider the whole picture. Is this a wise step to take? Any possible alternative to virtual functions will introduce maintainability problems, i.e., difficulty to modify and even to understand code.
The question is: is this really necessary? Will it really compensate?
Virtual functions involve derreferencing two pointers instead of only one. And yes, it is true it won't be inlined. I don't think, however, this being a real issue. I would indeed concentrate in algorithm-level optimization, and waste all other approaches before removing virtual funcions.
Take into account that at least one solution involves converting virtual functions to regular functions (not member functions), removing the well-known advantage of a virtual function (i.e., the class of the object itself) vs. a chain of if's.
That's said, it is your call.
Since you seem to have a fixed number types, it seems a reasonable approach would be the use of one vector per type and applying the operations separately for each type: processing a sequence of heterogeneous objects will amount to some disruption whether it is using virtual functions are not. Putting the framework of how the respective objects are called into a function template will conveniently deal with the commonality.
I have a user interface with a tree view on the left, and a viewer on the right (a bit like an email client). The viewer on the right displays the detail of whatever I have selected in the tree on the left.
The user interface has "add", "edit" and "delete" buttons. These buttons act differently depending on what "node" in the tree is selected.
If I have a node of a particular type selected, and the user clicks "edit", then I need to open the appropriate edit dialog for that particular type of node, with the details of that node.
Now, there's a lot of different types of node and implementing a visitor class feels a bit messy (currenty my visitor has about 48 entries....). It does work nicely though - basically for editing a have something like an OpenEditDialog class that inherits the visitor, and opens the appropriate edit dialog:
abstractTreeNode->accept(OpenEditDialog());
The problem is I have to implement the abstract visitor class for every "action" I want to perform on the node and for some reason I can't help thinking I'm missing a trick.
The other way could of been to implement the functions in the nodes themselves:
abstractTreeNode->openEditDialog();
I'm ording the node around a bit here, so maybe this is better:
abstractTreeNode->editClickedEvent();
I can't help but think this is polluting the node though.
I did think of a third way that I've not given that much thought yet. I could have a templated wrapper class that gets added to the tree instead which allows me to perhaps call free-functions to perform whatever actions, so I guess as it acts as a go between for nodes and interface:
(pseudo code off the top of my head just to give an idea):
template <class T>
TreeNode(T &modelNode)
{
m_modelNode = modelNode;
}
template <>
void TreeNode<AreaNode>::editClickedEvent()
{
openEditDialog(m_modelNode); // Called with concrete AreaNode
}
template <>
void TreeNode<LocationNode>::editClickedEvent()
{
openEditDialog(m_modelNode); // Called with concrete LocationNode
}
etc..
So this is effectively extending the nodes but in a different way to using the visitor and it seems a little bit neater.
Now before I go ahead and take the plunge using one of these methods, I thought it'd be wise to get some input.
Thanks! I hope all this makes some sense..
EDIT:
I've mocked up the templated wrapper idea..
class INode
{
public:
virtual ~INode() {}
virtual void foo() = 0;
};
class AreaNode : public INode
{
public:
AreaNode() {}
virtual ~AreaNode() {}
void foo() { printf("AreaNode::foo\r\n"); }
};
class RoleNode : public INode
{
public:
RoleNode() {}
virtual ~RoleNode() {}
void foo() { printf("RoleNode::foo\r\n"); }
};
class ITreeNode
{
public:
virtual ~ITreeNode() {}
virtual void bar() = 0;
virtual void foo() = 0;
};
template <class T>
class MainViewTreeNode : public ITreeNode
{
public:
MainViewTreeNode() : m_node() {}
virtual ~MainViewTreeNode() {}
void bar() {}
void foo() { m_node.foo(); }
protected:
T m_node;
};
template <>
void MainViewTreeNode<AreaNode>::bar()
{
printf("MainViewTreeNode<AreaNode>::bar\r\n");
}
template <>
void MainViewTreeNode<RoleNode>::bar()
{
printf("MainViewTreeNode<RoleNode>::bar\r\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
MainViewTreeNode<RoleNode> role;
MainViewTreeNode<AreaNode> area;
std::list<ITreeNode*> nodes;
nodes.push_back(&role);
nodes.push_back(&area);
std::list<ITreeNode*>::iterator it = nodes.begin();
for (; it != nodes.end(); ++it)
{
(*it)->foo();
(*it)->bar();
}
getchar();
return 0;
}
Thanks.
Visitor is useful when you have many operations and few types. If you have many types, but few operations, use normal polymorphism.
Instead of using m_node.foo(), what you should do is static inheritance. This is basically your "template wrapper" idea, but it's a well-established pattern.
class ITreeNode
{
public:
virtual ~ITreeNode() {}
virtual void bar() = 0;
virtual void foo() = 0;
};
template <class T>
class MainViewTreeNode : public ITreeNode
{
public:
MainViewTreeNode() : m_node() {}
virtual ~MainViewTreeNode() {}
void bar() {}
void foo() { m_node.foo(); }
protected:
T m_node;
};
becomes
class ITreeNode
{
public:
virtual ~ITreeNode() {}
virtual void bar() = 0;
virtual void foo() = 0;
};
template <class T>
class MainViewTreeNode : public ITreeNode
{
public:
MainViewTreeNode() {}
virtual ~MainViewTreeNode() {}
void bar() { T::bar(); }
void foo() { T::foo(); }
};
class RoleNode : public MainViewTreeNode<RoleNode> {
void bar() { std::cout << "Oh hai from RoleNode::bar()! \n"; }
void foo() { std::cout << "Oh hai from RoleNode::foo()! \n"; }
};
Of course, if you already have regular inheritance in the mix, why not just use that? There's not going to be any easier solution than normal polymorphism here. It works well when the number of types is high and the number of operations is low. Perhaps the flaw in your design is how many types you have.
Such problems are, unfortunately, all too common with C++ and statically typed OO languages in general. I recently stumbled into this article which describes how to implement double dispatch with a custom-made lookup table.
I can see a similar approach working here. Basically, you build a table of function wrappers of the type Entry below:
class EntryBase {
public:
virtual bool matches(TreeNode const &node) const = 0;
virtual void operator()(TreeNode &node) const = 0;
};
template<typename NodeType, typename Functor>
class Entry : public EntryBase {
Functor d_func;
public:
Entry(Functor func) : d_func(func) { }
virtual bool matches(TreeNode const &node) const {
return dynamic_cast<NodeType const *>(&node) != 0;
}
virtual void operator()(TreeNode &node) const {
d_func(dynamic_cast<NodeType &>(node));
}
};
Each such table would then represent one type of Visitor (you can do this without Boost too, of course):
class NodeVisitor {
typedef boost::shared_ptr<EntryBase> EntryPtr;
typedef std::vector<EntryPtr> Table;
Table d_entries;
public:
template<typename NodeType, typename Functor>
void addEntry(Functor func) {
EntryPtr entry(new Entry<NodeType, Functor>(func));
d_entries.push_back(entry);
}
void visit(TreeNode &node) {
EntryPtr entry = lookup(node);
if (!entry)
return; // this Visitor doesn't handle this type
(*entry)(node);
}
private:
EntryPtr lookup(TreeNode &node) {
Table::const_iterator iter =
std::find_if(d_entries.begin(), d_entries.end(),
boost::bind(&EntryBase::matches, _1, boost::ref(node)));
if (iter != d_entries.end())
return *iter;
return 0;
}
};
Construction of a table would be something like this:
void addToCompany(CompanyNode &company) { ... }
void addToEmployee(EmployeeNode &employee) { ... }
NodeVisitor nodeAdder;
nodeAdder.addEntry<CompanyNode>(&addToCompany);
nodeAdder.addEntry<EmployeeNode>(&addToEmployee);
After all that work, you could simply write (without any additions to TreeNode or any class that inherits from TreeNode):
nodeAdder.visit(someNode);
The templates ensure that the dynamic_cast always succeeds, so it's quite safe. The largest drawback is, of course, that it's not the fastest in the world. But for opening a dialog, the user is probably the slower factor, so it should be quite fast enough.
I just implemented this visitor in my own project, and it is working like a charm!
Another pattern to consider here is the Command pattern. You make your nodes store a list of commands that all have GetName & Execute methods. When a node is selected you enumerate the collection and call GetName on each command to get the menu items' name and when a menu item is clicked you call Execute. This gives you ultimate flexibility, you can set up the commands when the tree is created or in each node type's constructor. Either way you get to reuse commands accross types and have varying numbers of commands for each type.
Generally though, my experience would suggest that both this and the visitor pattern are probably overkill in this case and simply putting virtual Add, Edit and Delete methods on the base tree node type is the way to go.
I am busy adding a generic observer mechanism to a legacy C++ application (using Visual Studio 2010, but not using .Net, so .Net delegates are out of the question).
In the design I want to separate the application-specific part as much as possible from the generic observer mechanism.
The most logical way of implementing observers seems this way:
class IDoThisObserver
{
public:
void handlDoThis(int arg1, int arg2) = 0;
};
For every type of observer (IDoThisObserver, IDoThatObserver, ...) the arguments of the methods (handleDoThis, handleDoThat) are different.
What remains in a generic way of storing the observers, like this:
template<typename T>
class ObserverContainer
{
public:
void addObserver (T &t) {m_observers.push_back(&t);}
private:
std::list<T*> m_observers;
};
Calling an observer can't be generalized since the arguments are different for every observer type.
An alternative way would be to 'pack' all arguments into one argument, like this:
struct DoThisInfo
{
DoThisInfo (int arg1, int arg2) : m_arg1(arg1), m_arg2(arg2) {}
int m_arg1;
int m_arg2;
};
And then define a more generic observer, like this:
template<typename T>
class IObserver
{
public:
void notify(const T &t) = 0;
};
And a collection of these observers would then become this:
template<typename T>
class ObserverContainer
{
public:
void addObserver (IObserver<T> &obs) {m_observers.push_back(&obs);}
private:
std::list<IObserver<T>*> m_observers;
};
Now, much more logic can be centrally added to this ObserverContainer, including calling all observers. The 'initiator' of the call only needs to create and fill in the notification structure.
Classes that want to inherit from multiple kinds of observers, need to do it like this:
class MyObserver : public IObserver<NotifyThis>, public IObserver<NotifyThat>
{
...
};
Which of these approaches (observers with multiple explicit arguments or with one struct argument) seems the best? Are there any advantages or disadvantages to either of these approaches?
EDIT: I looked a bit further to alternative approaches, and the Slot/Signal approach seems another good candidate. Are there any important disadvantages in Slot/Signal that I should know of?
Why not just do:
class IObserver {
// whatever is in common
};
class IDoThisObserver : public IObserver
{
public:
void handlDoThis(int arg1, int arg2) = 0;
};
class IDoThatObserver : public IObserver
{
public:
void handlDoThat(double arg1) = 0;
};
?
Then you have:
class ObserverContainer
{
public:
void addObserver (IObserver* t) {m_observers.push_back(t);}
private:
std::list<IObserver*> m_observers;
};
The design with the struct argument is definitely better as it allows for generic code to be written in the ObserverContainer. It's generally a good design practice to replace longish argument lists with objects that encapsulate the arguments and this is a good example of the payoff. By creating a more general abstraction for your notify method (with the struct you're defining notify as a method that takes a chunk of "data" whereas with the arg list you're defining a method that takes two numbers) you allow yourself to write generic code that uses the method and doesn't have to concern itself with the exact composition of the passed in chunk of data.
Have you looked into Boost.Signals? Better than to reimplement the wheel.
As for Parameters: Calling an observer/slot should conceptionally be the same as if you would call an ordinary function. Most SignalSlots-Implementations allow multiple Parameters, so use it. And please use different signals for different observer types, then there is no need to pass around data in Variants.
Two Disadvantages of the Observer-Pattern/SignalSlots i have seen:
1) Program flow is difficult or even impossible to understand by looking only at the source.
2) Heavily dynamic programs with lots of Observers/SignalSlots may encounter a "delete this"
Everything aside, i like Observers/SignalSlots more than subclassing and thus high coupling.
I don't think either of your approaches would fit your requirement as is. However a little modification using a DataCarrier containing the dataset passed across all the observers wherein each observer would know what to read would do the trick. The sample code below might clear it (note i have not compiled)
enum Type {
NOTIFY_THIS,
NOTIFY_THAT
};
struct Data {
virtual Type getType() = 0;
};
struct NotifyThisData: public Data {
NotifyThisData(int _a, int _b):a(_a), b(_b) { }
int a,b;
Type getType() { return NOTIFY_THIS; }
};
struct NotifyThatData: public Data {
NotifyThatData(std::string _str):str(_str) { }
std::string str;
Type getType() { return NOTIFY_THAT; }
};
struct DataCarrier {
std::vector<Data*> m_TypeData;
};
class IObserver {
public:
virtual void handle(DataCarrier& data) = 0;
};
class NotifyThis: public virtual IObserver {
public:
virtual void handle(DataCarrier& data) {
vector<Data*>::iterator iter = find_if(data.m_TypeData.begin(), data.m_TypeData.end(), bind2nd(functor(), NOTIFY_THIS);
if (iter == data.m_TypeData.end())
return;
NotifyThisData* d = dynamic_cast<NotifyThisData*>(*iter);
std::cout << "NotifyThis a: " << d->a << " b: " << d->b << "\n";
}
};
class NotifyThat: public virtual IObserver {
public:
virtual void handle(DataCarrier& data) {
vector<Data*>::iterator iter = find_if(data.m_TypeData.begin(), data.m_TypeData.end(), bind2nd(functor(),NOTIFY_THAT);
if (iter == data.m_TypeData.end())
return;
NotifyThatData* d = dynamic_cast<NotifyThatData*>(*iter);
std::cout << "NotifyThat str: " << d->str << "\n";
}
};
class ObserverContainer
{
public:
void addObserver (IObserver* obs) {m_observers.push_back(obs);}
void notify(DataCarrier& d) {
for (unsigned i=0; i < m_observers.size(); ++i) {
m_observers[i]->handle(d);
}
}
private:
std::vector<IObserver*> m_observers;
};
class MyObserver: public NotifyThis, public NotifyThat {
public:
virtual void handle(DataCarrier& data) { std::cout << "In MyObserver Handle data\n"; }
};
int main() {
ObserverContainer container;
container.addObserver(new NotifyThis());
container.addObserver(new NotifyThat());
container.addObserver(new MyObserver());
DataCarrier d;
d.m_TypeData.push_back(new NotifyThisData(10, 20));
d.m_TypeData.push_back(new NotifyThatData("test"));
container.notify(d);
return 0;
}
This way u need to modify only the enum if u add a new structure.
Also u can use boost::shared_ptr to handle the mess of pointers.
I wouldn't get the syntax right so I'm just going to list the declarations to illustrate the structures. A generic Observer could be made to expect a parameter that is either subclassed to specific forms of your required parameters or is struct including a horizontal mapping of all primitive parameters that will be required by your Observers. Then the ObserverContainer could function as an AbstractFactory and each subclass of the ObserverContainer could be DoThatObserverFactory and DoThisObserverFactory. The factory would build an observer and assign a configuration to the observer to tell it which parameter to expect.
class AbstractObserverFactory {...};
class DoThatObserverFactory : AbstractObserverFactory {...};
class DoThisObserverFactory : AbstractObserverFactory {...};
class ObserverParam {...};
class DoThatObserverParam : ObserverParam {...};
class DoThisObserverParam : ObserverParam {...};
class Observer;
class DoThisObserver : public Observer
{
public:
void handlDoThis(DoThisObserverParam);
};
I'm trying to replace simple enums with type classes.. that is, one class derived from a base for each type. So for example instead of:
enum E_BASE { EB_ALPHA, EB_BRAVO };
E_BASE message = someMessage();
switch (message)
{
case EB_ALPHA: applyAlpha();
case EB_BRAVO: applyBravo();
}
I want to do this:
Base* message = someMessage();
message->apply(this); // use polymorphism to determine what function to call.
I have seen many ways to do this which all seem less elegant even then the basic switch statement. Using dyanimc_cast, inheriting from a messageHandler class that needs to be updated every time a new message is added, using a container of function pointers, all seem to defeat the purpose of making code easier to maintain by replacing switches with polymorphism.
This is as close as I can get: (I use templates to avoid inheriting from an all-knowing handler interface)
class Base
{
public:
template<typename T> virtual void apply(T* sandbox) = 0;
};
class Alpha : public Base
{
public:
template<typename T> virtual void apply(T* sandbox)
{
sandbox->applyAlpha();
}
};
class Bravo : public Base
{
public:
template<typename T> virtual void apply(T* sandbox)
{
sandbox->applyBravo();
}
};
class Sandbox
{
public:
void run()
{
Base* alpha = new Alpha;
Base* bravo = new Bravo;
alpha->apply(this);
bravo->apply(this);
delete alpha;
delete bravo;
}
void applyAlpha() {
// cout << "Applying alpha\n";
}
void applyBravo() {
// cout << "Applying bravo\n";
}
};
Obviously, this doesn't compile but I'm hoping it gets my problem accross.
Well, after giving in to dynamic_cast and multiple inheritance, I came up with this thanks to Anthony Williams and jogear.net
class HandlerBase
{
public:
virtual ~HandlerBase() {}
};
template<typename T> class Handler : public virtual HandlerBase
{
public:
virtual void process(const T&)=0;
};
class MessageBase
{
public:
virtual void dispatch(HandlerBase* handler) = 0;
template<typename MessageType>
void dynamicDispatch(HandlerBase* handler, MessageType* self)
{
dynamic_cast<Handler<MessageType>&>(*handler).process(*self);
}
};
template<typename MessageType> class Message : public MessageBase
{
virtual void dispatch(HandlerBase* handler)
{
dynamicDispatch(handler, static_cast<MessageType*>(this));
}
};
class AlphaMessage : public Message<AlphaMessage>
{
};
class BravoMessage : public Message<BravoMessage>
{
};
class Sandbox : public Handler<AlphaMessage>, public Handler<BravoMessage>
{
public:
void run()
{
MessageBase* alpha = new AlphaMessage;
MessageBase* bravo = new BravoMessage;
alpha->dispatch(this);
bravo->dispatch(this);
delete alpha;
delete bravo;
}
virtual void process(const AlphaMessage&) {
// cout << "Applying alpha\n";
}
virtual void process(const BravoMessage&) {
// cout << "Applying bravo\n";
}
};
int main()
{
Sandbox().run();
return 0;
}
It looks like you are trying to find some sort of double-dispatch system. Look into the Visitor pattern or other multiple-dispatch systems.
Your Bravo and Alpha classes are actually closures... Too bad C++ does not support them directly.
You could use a member pointer to do this:
typedef void (Sandbox::*SandboxMethod)();
struct BrAlpha {
BrAlpha(SandboxMethod method) : method(method){}
void apply(Sandbox sb){sb->*method();}
};
BrAlpha alpha(&Sandbox::applyAlpha);
BrAlpha bravo(&Sandbox::applyBravo);
(syntax may not be exact, but you know hat I mean)
I don't necessarily have an answer for your design pattern issue (though Modern C++ Design has a lot to say about it), but I do want to address your switch vs inheritance comment.
The problem with that simple swtich statement is maintainability. If that switch statement were in 1 location, then it's probably about the same amount of typing to create classes and inherit, but that switch statement is still a ticking time-bomb awaiting yet another state added without adding a case for it. If you assert the default:, you'll catch it at run time - eventually, but that's very poor. If you setup a bunch of function pointers and compile time assert on the table's size, you're doing better, but that's another level deeper than the switch statement. And this all goes out the window as soon as you have a second place in the code that needs to check state.
It's just that much easier once you have your interface class setup to let the compiler handle all the junk code of switching on states internally. You add the class need not worry about any other code as long as you follow the interface.