Defined in header <rome/delegate.hpp>
.
template<typename Signature, typename Behavior = target_is_expected>
class fwd_delegate; // undefined
template<typename... Args, typename Behavior>
class fwd_delegate<void(Args...), Behavior>;
template<typename Signature>
using event_delegate = fwd_delegate<Signature, target_is_optional>;
template<typename Signature>
using command_delegate = fwd_delegate<Signature, target_is_mandatory>;
Instances of class template rome::fwd_delegate
can store and invoke any callable target -- functions, lambda expressions, std::function, other function objects, as well as static and non-static member functions.
The rome::fwd_delegate
has identical functionality as rome::delegate
, but with the restriction that data can only be forwarded. To ensure this, only function signatures with void
return and arguments of immutable type are supported. E.g. the signature void(const std::string&)
would work, while void(int*)
or bool()
would not compile.
See rome::delegate
for a description of the functionality.
-
Args...
The argument types of the target being called. Must be immutable. -
Behavior
Defines the behavior of an emptyrome::fwd_delegate
being called. Defaults torome::target_is_expected
.The behavior can be chosen by declaring the delegate with one the following types:
-
rome::target_is_expected
A valid target is expected to be assigned before therome::fwd_delegate
is called.
When an emptyrome::fwd_delegate
is being called:- Throws a
rome::bad_delegate_call
exception. - Instead calls
std::terminate
, if exceptions are disabled.
- Throws a
-
rome::target_is_optional
Assigning a target to therome::fwd_delegate
is optional. Calling an empty delegate returns directly without doing anything. -
rome::target_is_mandatory
Prevents by design that arome::fwd_delegate
can be empty. This has following consequences:- Default constructor is deleted. A new instance of
rome::fwd_delegate
can only be created by passing a target to the constructor or by using one of the factory functions create. - There is no possibility to drop a currently assigned target, though it can be overridden by assigning a new target.
Note: The
rome::fwd_delegate
still becomes empty after a move, i.e., afterauto y = std::move(x)
x is empty and behaves as ifBehavior
was set torome::target_is_expected
. - Default constructor is deleted. A new instance of
-
rome::command_delegate
This delegate was designed for event or message-driven architectures to command an action that shall happen. Because the execution of the command is mandatory, a target must be assigned during construction of the delegate and can only be overriden by another target afterwards.
Arome::fwd_delegate
withBehavior
set torome::target_is_mandatory
.rome::event_delegate
This delegate was designed for event or message-driven architectures, to notify about happened events. Thus, it is optional whether someone wants to listen to the event or not.
Arome::fwd_delegate
withBehavior
set torome::target_is_optional
.
See rome::delegate
See rome::delegate
Model of an extremely simplified cruise control system. The four classes Engine, BrakingSystem, SpeedSensor and CruiseControl are atomic, i.e., are free from dependencies to other classes. Integration integrates all four.
See the code in examples/cruise_control.cpp.
#include <iostream>
#include <rome/delegate.hpp>
struct Engine {
void accelerate() {
std::cout << "engine accelerating\n";
}
void turnOff() {
std::cout << "engine turned off\n";
}
};
struct BrakingSystem {
void turnBrakesOn() {
std::cout << "brakes on\n";
}
void turnBrakesOff() {
std::cout << "brakes off\n";
}
};
struct SpeedSensor {
// Assigning delegate is optional for speed sensor to work.
rome::event_delegate<void(float)> onSpeedChanged;
};
class CruiseControl {
float targetSpeed_ = 0.0F;
// Assigning both delegates is required for cruise control to work.
rome::command_delegate<void()> onAccelerateCar_;
rome::command_delegate<void()> onSlowDownCar_;
public:
void updateAcceleration(const float drivingSpeed) {
if (drivingSpeed < targetSpeed_ * 0.95F) {
onAccelerateCar_();
}
else if (drivingSpeed > targetSpeed_ * 1.05F) {
onSlowDownCar_();
}
}
void setTargetSpeed(const float targetSpeed) {
targetSpeed_ = targetSpeed;
}
CruiseControl(rome::command_delegate<void()>&& onAccelerateCar,
rome::command_delegate<void()>&& onSlowDownCar)
: onAccelerateCar_{std::move(onAccelerateCar)}, onSlowDownCar_{std::move(onSlowDownCar)} {
}
};
struct Integration {
SpeedSensor speedSensor;
CruiseControl cruiseControl;
Engine engine;
BrakingSystem brakes;
Integration()
: cruiseControl{
[this]() {
brakes.turnBrakesOff();
engine.accelerate();
},
[this]() {
engine.turnOff();
brakes.turnBrakesOn();
}} {
speedSensor.onSpeedChanged = [this](float drivingSpeed) {
cruiseControl.updateAcceleration(drivingSpeed);
};
}
};
Integration integration{};
int main() {
// Simulate IO not connected in this example
integration.cruiseControl.setTargetSpeed(25.0F);
integration.speedSensor.onSpeedChanged(20.0F);
integration.speedSensor.onSpeedChanged(25.0F);
integration.speedSensor.onSpeedChanged(30.0F);
}
Output:
brakes off
engine accelerating
engine turned off
brakes on
- rome::delegate
The same asrome::fwd_delegate
but without return and argument type restrictions. - std::move_only_function (C++23)
Wraps a callable object of any type with specified function call signature. - std::function (C++11)
Wraps a callable object of any copy constructible type with specified function call signature.