Context
There is a modular C++ application. Each module provides an interface (an abstract class) which is used by other modules. There are no circular dependencies. The module interaction happens via direct method calls in one way, and via events (a variation of the observer pattern, similar to Qt's signals) in the other way.
For example:
// an interface towards the battery manager
class IBatteryManager
{
public:
// get the battery level
virtual int batteryLevel() = 0;
// the system is about to run out of battery and enter
// a power saving mode
virtual Event<> &aboutToRunOutOfBattery() = 0;
};
The Event
class looks like this:
template<typename... tArgs>
class Event
{
public:
// client: the provided function will be called when this
// event is emitted
void subscribe(std::function<void(tArgs...)>);
// server: emit the event
void notify(tArgs...);
};
Problem and Possible Solution
Sometimes a fire-and-forget event is not enough. A module might want to notify its clients about some event and wait until it is processed (and maybe get back some values).
For instance, BatteryManager
might want to wait until all clients have processed the about-to-run-out-of-battery event before entering the power saving mode.
I can imagine multiple ways how this can be done. One of them would be to extend the Event
class with the future/promise pattern in the following way:
template<typename tValue, typename... tArgs>
class Event
{
public:
// client: now must return some value
void subscribe(std::function<Future<tValue>(tArgs...)>);
// server: this future is fulfilled as soon as all clients
// have completed their promises
Future<std::vector<tValue>> notify(tArgs...);
};
Questions
Updated: I have rephrased questions a bit to be more clear.
Is there a design pattern which describes the approach shown above? In other words, does such approach have a commonly accepted name, vocabulary, pros/cons, a canonical implementation, a list of things one needs to pay attention to, and other stuff what patterns usually have?
I am asking because this might be not a problem which is unique to me. So I do not want to end up reinventing the wheel, both in terms of implementation (by probably re-doing someone else's mistakes) and terminology (by introducing my own terms, which will make the communication with other developers harder).
Secondary questions: Are there maybe better approaches? What would be the best practice in this case?
void
with an actual return value)?