I'm writing some arduino libraries and would like to improve readability / add some syntactic suggar.
What I would like to do is create objects on the heap in a way that would look like:
Panel panel(
Button( 1 ).on( Click( clickfunc ) ),
Button( 2 ).on( Hold( holdfunc, 1000 ) )
);
(Button, Click, Hold are all classes and internally managed via linked lists (so they aren't constant.))
I tried writing it this way but I stumbled over problems with references to temporaries.
Currently I can use:
Button button1( 1 ), button2( 2 );
Click theClick( clickFunction );
Hold theHold( holdFunction, 1000 );
Panel( button1.on( theClick ), button2.on( theHold ) );
but this is not nearly as readable as the above and tends to be error-prone because you have to stay alert and don't put e.g. theClick on another button which would break the linked list.
Some heavily shortened excerpts from the classes like they are now.
class Button {
Handler *_first;
Button( int no ){...}
Button & on( Handler &handler ){
handler._next = _first;
_first = &handler;
return *this;
}
void handle( int oldValue, int newValue ) {
Handler *handler;
for( handler = _first; handler; handler = handler->_next ){
handler->handle( oldValue, newValue );
}
}
}
class Handler {
Handler *_next;
virtual void handle( int oldValue, int newValue ) = 0;
...
}
class Click : public Handler {
...
}
class Hold : public Handler {
...
}
Note that this doesn't necessarily needs to stay this way. The goal is to provide a library where its user doesn't need to know to much about its inner working but has a simple/clean interface.
If you have problem with dangling references with the code above, I suspect you are making a linked list that create references (or pointer) that points to those element on the stack.
I suspect also that your signature looks like this:
Button& on(const Event& event) { /* ... */ }
To help you with your problem, I suggest to change the signature of your on function to something like this:
template<typename EventType>
Button& on(EventType&& event) {
}
That way, you can actually forward the object into the heap, and use some form of type easure to put it into your linked list:
struct Handler {
virtual void handle(int oldValue, int newValue) = 0;
// Defaulted virtual destructor
virtual ~Handler() = default;
};
template<typename T>
struct HandlerImpl : Handler {
// constructors
HandlerImpl(T h) : handler{std::forward<T>(h)} {}
void handle(int oldValue, int newValue) {
handler.handle(oldValue, newValue);
}
// We use the compiler generated destructor
private:
remove_rvalue_reference_t<T> handler;
};
template<typename HandlerType>
Button& on(HandlerType&& event) {
// See the code example below
}
What changes in the rest of your code?
Well, now both syntax you posted are supported. The first syntax will move and hold the variable. The second syntax will only hold references to the events and will assume that the lifetime of the event are equal or bigger of those of the button.
Also, Click and Hold don't need to extend any class nor need virtual function or virtual destructors.
If you don't want the second syntax to hold references and use copy instead, replace remove_rvalue_reference_t by std::remove_reference_t.
This pattern I showed you can be applied for Button, and for any widget type you want.
Here's how remove_rvalue_reference_t is implemented:
template<typename T> struct remove_rvalue_reference { using type = T; };
template<typename T> struct remove_rvalue_reference<T&&> { using type = T; };
template<typename T> using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;
Since you have posted an example of your code, I can now help you transform it so it can work with the code above.
First, liked list are slow, and hand rolled liked list are worse. I strongly suggest you to use std::vector. Secondly, std::unique_ptr is the preferred way to hold owning pointers. So, just by following this and the steps mentioned above, your code should look like this:
struct Button {
std::vector<std::unique_ptr<Handler>> _handlers;
Button(int no) { /* ... */ }
// This function will work for any type that
// happen to have an `handle` function.
template<typename H> // <--- H is the handler type
Button& on(H&& handler) { // H&& in this case means forwarding reference.
// We add (emplace) a new HandlerImpl, allocated on the heap using `std::make_unique`
_handlers.emplace_back(
std::make_unique<HandlerImpl<H>>(std::forward<H>(handler))
);
return *this;
}
void handle(int oldValue, int newValue) {
// We use a range for loop here to iterate on the vector
for (auto&& handler : _handlers) {
handler->handle(oldValue, newValue);
}
}
};
// We do not extends anything
struct Click {
// Notice that the function is not virtual
void handle(int oldVal, int newVal) {/* ... */}
};
struct Hold {
void handle(int oldVal, int newVal) {/* ... */}
};
Here's a live example at Coliru
Related
I am going from C development to C++ on the STM32 platform and simply cant find a suitable solution for my problem.
Please have a look at the simplified example code attached to this post.
#include <iostream>
#include <functional>
#include <list>
using namespace std;
class Pipeline {
public:
std::list<std::function<void(Pipeline*)>> handlers;
//add handler to list --> works fine
void addHandler(std::function<void(Pipeline*)> handler) {
this->handlers.push_front(handler);
}
void ethernetCallback(void) {
//handle received data and notify all callback subscriptions --> still works fine
// this callback function is normally sitting in a child class of Pipeline
int len = handlers.size();
for (auto const &handler : this->handlers) {
handler(this);
}
}
void removeHandler(std::function<void(Pipeline*)> handler) {
// Here starts the problem. I can not use handlers.remove(handler) here to
// unregister the callback function. I understood why I can't do that,
// but I don't know another way of coding the given situation.
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout<<"I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
p->addHandler(std::bind(&Engine::callback, this, std::placeholders::_1));
}
};
int main()
{
Engine *e = new Engine();
Pipeline *p = new Pipeline();
e->assignPipelineToEngine(p);
// the ethernet callback function would be called by LWIP if new udp data is available
// calling from here for demo purposes only
p->ethernetCallback();
return 0;
}
The idea is that when the class "Pipeline" receives new data over ethernet, it informs all registered callback functions by calling a method. The callback functions are stored in a std::list. Everything works fine till here, but the problem with this approach is that I can't remove the callback functions from the list, which is required for the project.
I know why I can't simply remove the callback function pointers from the list, but I don't know another approach at the moment.
Probably anybody could give me a hint where I could have a look for solving this problem. All resources I've researched don't really show my specific case.
Thank you all in advance for your support! :)
One option would be to have addHandler return some sort of identifier that can later be passed to removeHandler. For example:
class Pipeline {
public:
std::map<int, std::function<void(Pipeline*)>> handlers;
int nextId = 0;
//add handler to list --> works fine
void addHandler(std::function<void(Pipeline*)> handler) {
handlers[nextId++] = handler;
}
void ethernetCallback(void) {
for (auto const& entry : handlers) {
entry.second(this);
}
}
void removeHandler(int handlerToken) {
handlers.erase(handlerToken);
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout<<"I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
handlerToken = p->addHandler(
std::bind(
&Engine::callback,
this,
std::placeholders::_1
)
);
}
void unregisterPipelineFromEngine(Pipeline *p) {
p->removeHandler(handlerToken);
}
private:
int handlerToken;
};
Perhaps you could attach an ID to each handler. Very crude variant would just use this address as an ID if you have at most one callback per instance.
#include <functional>
#include <iostream>
#include <list>
using namespace std;
class Pipeline {
public:
using ID_t = void *; // Or use integer-based one...
struct Handler {
std::function<void(Pipeline *)> callback;
ID_t id;
// Not necessary for emplace_front since C++20 due to agreggate ctor
// being considered.
Handler(std::function<void(Pipeline *)> callback, ID_t id)
: callback(std::move(callback)), id(id) {}
};
std::list<Handler> handlers;
// add handler to list --> works fine
void addHandler(std::function<void(Pipeline *)> handler, ID_t id) {
this->handlers.emplace_front(std::move(handler), id);
}
void ethernetCallback(void) {
// handle received data and notify all callback subscriptions --> still
// works fine
// this callback function is normally sitting in a child class of
// Pipeline
int len = handlers.size();
for (auto const &handler : this->handlers) {
handler.callback(this);
}
}
void removeHandler(ID_t id) {
handlers.remove_if([id = id](const Handler &h) { return h.id == id; });
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout << "I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
//p->addHandler(std::bind(&Engine::callback, this, std::placeholders::_1), this);
//Or with a lambda
p->addHandler([this](Pipeline*p){this->callback(p);},this);
}
void removePipelineFromEngine(Pipeline *p) { p->removeHandler(this); }
};
int main() {
Engine *e = new Engine();
Pipeline *p = new Pipeline();
e->assignPipelineToEngine(p);
// the ethernet callback function would be called by LWIP if new udp data is
// available calling from here for demo purposes only
p->ethernetCallback();
return 0;
}
You might also consider std::map<ID_t,std::function<...>> instead of list, not sure how memory/performance constrained you are.
Obligatory: do not use new, use std::unique_ptr, or better use automatic storage whenever you can. Although in this case a pointer is appropriate for e as you need stable address due to this capture/bind/ID.
std::functions are not comparable as there isn't a good generic way how to define this comparison.
I've written the most basic Event System I can think of. I come from a javascript background so I followed the On, Off, Emit syntax. The intention is to be able to create an EventSystem that can Emit any type of derived Event object and have the appropriate handlers called.
Please note, I am stuck in C++98 for reasons
So far my best idea is to have a simple Event object and a typedef for each Event type to handle it.
class Event {};
class AlarmEvent : Event {};
class ErrorEvent : Event {};
typedef void (*EventHandler)(Event event);
typedef void (*AlarmEventHandler)(AlarmEvent event);
typedef void (*ErrorEventHandler)(ErrorEvent event);
My issue is I want my modules to be able to attach as easy as possible.
int main()
{
Module module;
EventSystem es;
Event shutdown_event("shutdown");
AlarmEvent alarm_event("alarm", "Oh crap");
es.On("shutdown", module.OnEvent);
es.On("shutdown", module.OnEvent);
es.On("alarm", module.OnAlarmEvent);
es.Emit(shutdown_event);
es.Emit(alarm_event);
}
But looking at the EventSystem
class EventSystem {
public:
void On(std::string id, EventHandler handler);
void Emit(Event event);
void GetEventHandlers(std::string id, std::vector<EventHandler> *&handlers);
std::map<std::string, std::vector<EventHandler> > events;
};
I'd need an On, GetEventHandlers, and events property for every event type. This would quickly become terrible. Is there a better path where I can use a template to allow EventSystem to stay as simple as possible?
C++98 is old, older than variadic templates. The following emulates variadic templates with linked lists, which is very much suboptimal, but it should work.
// linked lists for "variadic" templates
struct Nil { };
template<typename X, typename XS>
struct Cons { };
// utility type
struct BlackHole {
template<typename T>
BlackHole(const T&) { }
};
// anything can be converted to a BlackHole implicitly, but it's a "worse"
// conversion than being converted to a base class
// I would template your event system over every event type
// this implementation only works properly if more derived events appear before their bases
template<typename Events> // e.g. Events = Cons<AlarmEvent, Cons<ErrorEvent, Cons<Event, Nil>>>
class EventSystem;
template<>
class EventSystem<Nil> {
protected:
// see below for Emit/EmitEmitted thing
// usage of BlackHole means that e.g. if calling with AlarmEvent
// and only overloads for Event and BlackHole are visible
// then the former will be chosen, since the latter conversion is worse
// can't just say template<typename T> EmitEmitted(T const&) { }
void EmitEmitted(BlackHole) { }
public:
// these overloads exist so the using declarations ahead don't fail
// for maximum type-safety, create a private type and
// make it an argument of each, so they can never be called
// using Emit/EmitEmitted creates type safety; again, see below
void Emit() { }
// On has easy type safety: you just can't call it for an unknown type
void On() { }
// GetEventHandlers doesn't really make sense anyway
// I don't think you need it, you can't have a vector of mixed handlers
// so why bother?
};
template<typename X, typename XS>
class EventSystem<Cons<X, XS> > : public EventSystem<XS> {
std::vector<void (*)(X)> handlers;
protected:
// "forward" all the EmitEmitted overloads made for XS
using EventSystem<XS>::EmitEmitted;
// overload for the specific case of an X
void EmitEmitted(X x) {
// fire all of the X-specific handlers
for(typename std::vector<void (*)(X)>::iterator i = handlers.begin(); i != handlers.end(); ++i) {
(*i)(x);
}
// call the rest of the handlers
EventSystem<XS>::EmitEmitted(x);
}
public:
// more "forwarding"
using EventSystem<XS>::Emit;
void Emit(X x) {
return EmitEmitted(x);
}
// suppose you have an EventSystem<Cons<std::string, Nil> >
// if you Emit an int, say, then you want this to fail
// thus the overload of Emit in EventSystem<Nil> should not be
// a catch-all or anything
// however, if you emit a std::string, then you need to recursively
// emit from EventSystem<Nil>, to handle any handlers for superclasses
// now you don't want it to explode
// solution? two functions
// Emit is the public entry point, and fails on unknown types
// EmitEmitted is named so because, once it's called, the type
// is known to be known, and will/has been emitted by at least one layer
// it no-ops once the base case is reached
// it is protected, and it is where the actual logic is
// easy now, right?
using EventSystem<XS>::On;
void On(void (*handler)(X)) {
handlers.push_back(handler);
}
};
Example usage:
struct Event {
std::string message;
Event(std::string message) : message(message) { }
};
void HandleEvent(Event e) {
std::cerr << e.message << "\n";
}
class AlarmEvent : public Event {
int hour;
int minute;
static std::string BuildMessage(int hour, int minute) {
std::stringstream builder;
builder << "Alarm: " << std::setfill('0');
builder << std::setw(2) << hour << ":";
builder << std::setw(2) << minute;
return builder.str();
}
friend void HandleAlarm(AlarmEvent);
public:
AlarmEvent(int hour, int minute) : Event(BuildMessage(hour, minute)), hour(hour), minute(minute) { }
};
void HandleAlarm(AlarmEvent a) {
// please ignore the fact that this is very stupid
if((a.hour + (a.minute / 60)) % 24 < 12) std::cerr << "AM Alarm\n";
else std::cerr << "PM Alarm\n";
}
struct ErrorEvent : Event {
ErrorEvent(std::string message) : Event(message) { }
};
void HandleError(ErrorEvent) {
static int count = 1;
std::cerr << "Error " << count++ << "\n";
}
int main() {
EventSystem<Cons<AlarmEvent, Cons<ErrorEvent, Cons<Event, Nil> > > > system;
// all handled by overload resolution
// no need to say what type you're dealing with
system.On(HandleEvent);
system.On(HandleAlarm);
system.On(HandleError);
// doesn't work
// system.On(std::exit)
system.Emit(ErrorEvent("Bad things"));
system.Emit(AlarmEvent(2, 30));
system.Emit(Event("Something happened"));
system.Emit(ErrorEvent("More bad things"));
system.Emit(AlarmEvent(11, 67));
// doesn't work
// system.Emit(5);
}
Not sure that all the example code is C++98, but that doesn't matter. It appears to work nicely. Also, there's a lot of copying going on here. It may be advisable to change handlers from void (*)(T) (which necessitates a copy) to void (*)(T&) or void (*)(T const&).
If your functions took references or pointers, then you would be able to pass polymorphic child types into them. So then you only need one type of function ptr.
typedef void (*EventHandler)(Event& event);
or
typedef void (*EventHandler)(Event* event);
I have a bit of a design problem:
I have a class describing a Robot; It can move to different directions, move a camera to different views etc. It looks something like this:
class Robot {
private:
...
public:
void move_right();
void move_left();
void switch_camera()
void raise_camera()
}
I want to add another method which performs a series of events. Thing is, I need able to abort the events midway.
I do want to clarify that the robot is running on a micro controller and not on a standard OS - so I can't really send a signal to the process or anything.
My first idea was to store the event functions in an array and iterate over it:
#typedef void(robo_event *)(void)
robo_event next_event;
robo_event *event_sequence;
Robot() {
this->next_event = nullptr;
}
void perform_event_series() {
for(this->next_event = *event_sequence; this->next_event != nullptr; this->next_event+=sizeof(robo_event)) {
this->next_event();
}
}
void abort_event_series() {
this->next_event = nullptr;
}
Thing is, the c++ standard forbids storing addresses of member functions, so this is starting to get awkward. I can make the functions static, but I do need to use them quite frequently and that would still be awkward. I want to be able to change to event sequence without too much work if changes are yet to come, so I thought that saving those on some sort of array / vector would be the best.
Any help with c++ member function syntax / better ideas on how to approach this problem would be much appreciated.
Thing is, the c++ standard forbids storing addresses of member functions
C++ most certainly allows you to store pointers to member functions (and variables), but the syntax is a bit different to accommodate the this pointer type, virtual functions, inheritance, etc.
class Example
{
public:
double foo(int x) { return x * 1.5; }
};
int main() {
double (Example::* member_function_ptr)(int);
member_function_ptr = &Example::foo;
Example example;
std::cout << (example.*member_function_ptr)(2) << std::endl;
}
If all your functions are for the same class, same return type, same arguments, etc. then you can make a table of them easy enough.
Storing pointers to member functions is perfectly allowable in c++:
#include <vector>
class Robot {
private:
public:
void move_right();
void move_left();
void switch_camera();
void raise_camera();
};
struct Action
{
Action(void (Robot::*what)(void))
: what(what)
{}
void perform(Robot& who) const
{
(who.*what)();
}
void (Robot::*what)(void);
};
bool should_abort();
void perform_actions(Robot& who, std::vector<Action> const& actions)
{
for (auto&& action : actions)
{
if (should_abort()) break;
action.perform(who);
}
}
int main()
{
std::vector<Action> actions {
&Robot::move_right,
&Robot::raise_camera,
&Robot::switch_camera,
&Robot::move_left
};
Robot r;
perform_actions(r, actions);
}
Pointers to functions are of different types to pointers to members.
You need void(Robot::*)(void) not void(*)(void).
class Robot {
private:
typedef void(Robot::*robot_event)(void)
robo_event next_event;
robo_event *event_sequence;
Robot() {
next_event = nullptr;
}
void perform_event_series() {
for(next_event = *event_sequence; next_event != nullptr; ++next_event) {
(this->*next_event)();
}
}
void abort_event_series() {
next_event = nullptr;
}
public:
void move_right();
void move_left();
void switch_camera()
void raise_camera()
}
This question has been asked multiple times but mine is a slightly different case. Say I have a std::vector of observers which I notify when a certain event happens:
void SomeClass::doThing() {
// do things ...
// notify observers
for (auto* o : mObservers) {
o->thingHappened();
}
}
What if in the implementation of thingHappened the observer calls a method in SomeClass to remove itself from the observers? What are some of the best ways to handle this?
One possibility is to make a copy of mObservers before the for loop and use it instead, but the extra copy can be wasteful.
Another possibility is to delegate changes to the array to be run after the loop is finished, perhaps setting a lock (just a boolean) before the loop starts and while this lock is set, the methods that mutate the vector delegate themselves to be called after the loop is done when lock is set to false (could be done with a vector of lambdas... quite cumbersome).
If you have control over the signature of thingHappened(), you can change it to return a bool indicating whether it should be removed. Then, you can remove all the values which return true (or false; depends on the semantics you want).
Luckily for us, std::remove_if and std::partition are guaranteed to call the predicate exactly once per object in the range.
void SomeClass::doThing() {
// do things ...
// notify observers
auto newEnd = std::remove_if(mObservers.begin(), mObservers.end(), [](auto *o) {
return o->thingHappened();
});
// assuming mObservers is a vector
mObservers.erase(newEnd, mObservers.end());
}
One way to work around this is to change the data structure. With a std::list the removal of a element only invalidates iterators/references/pointers to that element. Since the rest of the list remains intact all we need to do is get an iterator to the next element before we process the current one. That would look like
for (auto it = the_list.begin(); it != the_list.end();)
{
auto next = std::next(it);
it->call_the_possibly_removing_function();
it = next;
}
What if in the implementation of thingHappened the observer calls a method in SomeClass to remove itself from the observers? What are some of the best ways to handle this?
The following method has worked for me in the past.
Note that your are going to iterate over the observers.
When a client requests to remove an observer to be removed, check whether you are in the middle of iterating over the observers. If you are, set it aside in another vector. If not, remove it from the observers.
After you are done iterating over the observers, remove all the observers that need to be removed.
Note that you are done iterating over the observers.
void SomeClass::removeObserver(Observer* o) {
if ( this->isIterating )
{
observersToRemove.push_back(o);
}
else
{
// Code for real removal of the observer
}
}
void SomeClass::doThing() {
this->isIterating = true;
for (auto* o : mObservers) {
o->thingHappened();
}
for ( auto* o : observersToRemove )
{
// Code for real removal of the observer
}
observersToRemove.clear();
this->isIterating = false;
}
R Sahu's answer provides a flexible technique for solving this problem. The one thing that concerns me about it is the introduction of several variables that you have to manage. However, it's totally possible to wrap the functionality in a utility class.
Here's a sketch of what you could do:
#include <functional>
#include <utility>
#include <vector>
// Note that this is not threadsafe
template <typename Type>
class MutableLock {
bool locked = false;
Type value;
// std::function gives us a more general action,
// but it does come at a cost; you might want to consider using
// other techniques.
std::vector<std::function<void(Type&)>> actions;
public:
class AutoLocker {
MutableLock& lock;
friend class MutableLock<Type>;
explicit AutoLocker(MutableLock& lock)
: lock{ lock }
{
}
public:
~AutoLocker()
{
lock.unlock();
}
};
MutableLock() = default;
// The [[nodiscard]] is a C++17 attribute that
// would help enforce using this function appropriately
[[nodiscard]] AutoLocker lock()
{
locked = true;
return AutoLocker{ *this };
}
void unlock()
{
for (auto const& action : actions) {
action(value);
}
actions.clear();
locked = false;
}
template <typename F>
void action(F&& f)
{
if (!locked) {
f(value);
} else {
actions.emplace_back(std::forward<F>(f));
}
}
// There needs to be some way to expose the value
// not under the lock (so that we can use it when
// we call `lock()`).
//
// Even if your `Type` is not a range, this would
// be fine, as member functions of a template class
// aren't instantiated unless you call them.
//
// However, you may want to expose other ways to
// access the value
auto begin() { return std::begin(value); }
auto end() { return std::end(value); }
auto begin() const { return std::begin(value); }
auto end() const { return std::end(value); }
};
Using it would look something like this:
#include <algorithm>
#include <iostream>
class Observer {
public:
virtual void thingHappened() = 0;
protected:
~Observer() = default;
};
class SomeClass {
MutableLock<std::vector<Observer*>> observers;
public:
void addObserver(Observer* observer)
{
observers.action([observer](auto& observers) {
observers.push_back(observer);
});
}
void remove(Observer const* observer)
{
observers.action([observer](auto& observers) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
});
}
void doSomething()
{
auto lock = observers.lock();
for (auto* observer : observers) {
observer->thingHappened();
}
// when `lock` goes out of scope, we automatically unlock `observers` and
// apply any actions that were built up
}
};
class Observer1 : public Observer {
public:
SomeClass* thing;
void thingHappened() override
{
std::cout << "thing 1\n";
thing->remove(this);
}
};
int main()
{
SomeClass thing;
Observer1 obs;
obs.thing = &thing;
thing.addObserver(&obs);
thing.doSomething();
thing.doSomething();
}
On Coliru
I am trying to apply the Composite pattern, so I need to create a Leaf class and a Composite class, both inheriting from the same Component class. In order for any of my Components to perform their duty they need to ask help from a single Helper object. We have the following
struct Helper {
void provide_help();
};
struct Component {
Component(Helper* helper)
: m_helper(helper) {
}
virtual void operation() = 0;
// the call_for_help function will be used by subclasses of Component to implement Component::operation()
void call_for_help() {
m_helper->provide_help();
}
private:
Helper* m_helper;
};
And here are two different Leaf subclasses:
struct Leaf1
: Component {
Leaf1(Helper* helper)
: Component(helper) {
}
void operation() override {
call_for_help();
operation1();
}
void operation1();
};
struct Leaf2
: Component {
Leaf2(Helper* helper)
: Component(helper) {
}
void operation() override {
call_for_help();
operation2();
}
void operation2();
};
So far, so good. Now the Composite class is giving me grief. The typical implementation is as follows
struct Composite
: Component {
Composite(Helper* helper)
: Component(helper) {
}
void operation() override {
for (auto el : m_children) el->operation();
}
private:
std::vector<Component*> m_children;
};
which by going through the m_children one by one and calling operation on each essentially calls the helper function multiple times, even though one call is enough for all children. Ideally, if the m_children consisted, say, of a Leaf1 and a Leaf2, I would like somehow the Composite operation to call the helper function only once and then call in succession Leaf1::operation1() and then Leaf2::operation2(). Is there any way to achieve what I need? Alternative designs are welcome. I hope my question makes sense. Thanks in advance!
You want a polymorphic operation but you are adding more responability to the method (calling the helper). It's better to separate these two things.
struct Component {
void call_operation(){
call_for_help();
operation();
}
virtual void operation() = 0;
void call_for_help();
};
Remove the call_for_help() from leaf::operation() (making operation1, operation2 redundant, polymorphism) and the rest should work fine.
You can even hide operation() from your public interface, you'll need friendship with your Composite in that case.
As it could happen at any level, one approach could be to handle this at the level of the helper.
A sketch of the approach would be:
class Helper {
bool composite_help = false;
bool help_provided;
public:
void provide_help() {
if ((composite_help && !help_provided) || !composite_help) {
//TO DO: provide help
help_provided = true;
}
}
void start_composite_help() {
composite_help = true;
help_provided = false;
}
void end_composite_help() {
composite_help = false;
}
};
The principle is that the call for help performed by individual components works as before. But when the composite calls for help, you take preacutions to make sure that the call is performed only once:
void operation() override {
m_helper->start_composite_help();
for (auto el : m_children) el->operation();
m_helper->start_composite_help();
}
As said, this is only a sketch: the code provided as such will not work as soon as you have several levels of composites. So this needs to be improved:
instead of a bool composite_help you'd need a counter, which gets incremented when entering a composite operation and decremented when you exit it. In this case, the counter would go back to 0 (re-enabling help) only when the last level of composte has finished its job.
may be the helper performs different operations to provide help. So you could also imagine to have a "transaction id" that uniquely identifies a group of related operations, and you manage the counter not for the helper overall, in a map of active transactions.
finally, the start/end is not so nice. A RAII helper to the helper could make the whole setup more robust (for example when an exception breaks the normal execution flow.)
I think this problem would be better solved with a combination of Composite and Mediator.
Heads up! I'll show you a different version of the mediator pattern, which is not the same as the canonical version.
It's not of the business of your composite structure to know if a helper was called or not. You'd better do this using some kind of event handler.
Since you have only one helper, you could try like this:
class Helper {
public:
void callHelper() { std::cout << "Helper called" << std::endl; }
};
class Mediator {
private:
std::map<std::string, std::vector<Helper>> subscribers;
int updateLimit = -1;
int currentUpdateCount = 0;
void resetUpdateCount() {
currentUpdateCount = 0;
}
public:
Mediator(){}
void subscribe(std::string evt, Helper helper) {
subscribers[evt].push_back(helper);
}
void update(std::string evt) {
for (auto& h: subscribers[evt]) {
h.callHelper();
}
}
void setUpdateLimit(int i) {
updateLimit = i;
resetUpdateCount();
}
void removeUpdateLimit() {
updateLimit = -1;
resetUpdateCount();
}
int getUpdateLimit() {
return updateLimit;
}
void updateLimited(std::string evt) {
if (updateLimit < 0 || currentUpdateCount < updateLimit) {
update(evt);
currentUpdateCount++;
}
}
};
int main(int argc, const char *argv[])
{
Mediator m;
Helper h1, h2;
m.subscribe("bar", h1);
m.setUpdateLimit(1);
// Will be called only once
m.updateLimited("bar");
m.updateLimited("bar");
m.updateLimited("bar");
m.removeUpdateLimit();
return 0;
}
Using it:
Mediator m;
Helper h1, h2;
m.subscribe("bar", h1);
m.setUpdateLimit(1);
// Will be called only once
m.updateLimited("bar");
m.updateLimited("bar");
m.updateLimited("bar");
m.removeUpdateLimit();
So, here is what you do to integrate this to you composite structure. Remove the helper from you nodes, add the Mediator to the base class:
struct Component {
Component(Mediator& mediator)
: m_helper(mediator) {
}
virtual void operation() = 0;
// the call_for_help function will be used by subclasses of Component to implement Component::operation()
void notify() {
m_mediator->updateFiltered(Component::updateEventName);
}
static std::string updateEventName;
private:
Mediator& m_mediator;
};
std::string Component::updateEventName = "update.composite";
struct Leaf1
: Component {
Leaf1(Helper* helper)
: Component(helper) {
}
void operation() override {
notify();
operation1();
}
void operation1();
};
Using it:
Mediator m;
Helper h;
Composite c(m);
Leaf1 l1(m), l2(m);
c.add(l1);
c.add(l2);
m.subscribe(Component::updateEventName, h);
m.setUpdateLimit(1);
// Will be called only once, even if it has childrens
c.update();
m.removeUpdateLimit();
IMPORTANT: This solution is suboptimal, it has some issues, like you having to pass a mediator instance to every node constructor, but it's just a raw idea for you to work on.
Hope it helps!