Defined in header <rome/delegate.hpp>
.
template<typename Signature, typename Behavior = target_is_expected>
class delegate; // undefined
template<typename Ret, typename... Args, typename Behavior>
class delegate<Ret(Args...), Behavior>;
Instances of class template rome::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 stored callable object is called the target of rome::delegate
. If a rome::delegate
contains no target, it is called empty.
Invoking the target of an empty rome::delegate
results in a defined behavior configurable by the Behavior
template parameter (see Template parameters below).
Assigning a function object target to a rome::delegate
can be done during construction of or with an assignment to the rome::delegate
. Other callable targets such as functions and member functions either need to be wrapped by a function object (e.g. by a lambda expression) or use the provided factory method create
. See also the Examples below.
Small targets are stored in the local storage of a rome::delegate
. Such small object optimization takes place if:
- the target is a function object of size smaller or equal to
sizeof(void*)
e.g. a lambda expression capturing a pointer - the target is a function
- the target is a member function, both the member function pointer and the reference to the object are stored locally
If a function object target is bigger than sizeof(void*)
, new storage is dynamically allocated.
The size of a rome::delegate
is the size of an object pointer plus twice the size of a function pointer:
sizeof(rome::delegate<Ret(Args...), Behavior>)
== sizeof(void*) + 2*sizeof(void (*)())
A rome::delegate
is moveable but not copyable.
-
Ret
The return type of the target being called. -
Args...
The argument types of the target being called. -
Behavior
Defines the behavior of an emptyrome::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::delegate
is called.
When an emptyrome::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
(only ifRet
==void
)
Assigning a target to therome::delegate
is optional. Calling an empty delegate returns directly without doing anything.
Compile error, ifRet
!=void
. -
rome::target_is_mandatory
Prevents by design that arome::delegate
can be empty. This has following consequences:- Default constructor is deleted. A new instance of
rome::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::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
-
- constructor
constructs a newrome::delegate
instance - desctructor
destroys arome::delegate
instance - operator=
assigns or drops a target - swap
swaps the targets - operator bool
checks if a valid target is contained - operator()
invokes the target - create - static
creates a newrome::delegate
instance with given target assigned
- rome::swap
swaps the targets of tworome::delegate
instances - operator==, operator!=
comparesrome::delegate
with nullptr
The most similar C++ standard library counterpart is std::move_only_function (since C++23). It has similar behavior and interface, with following main differences:
- undefined behavior when called empty
- specifiable cv-qualifiers, ref-qualifiers and noexcept-specifiers for
operator()
- direct assignment of functions and member functions possible
Withrome::delegate
they need to be wrapped by a function object (e.g. lambda expression) or by using the create function. - unspecified storage size for small object optimization
Basic usage examples for all three types of Behavior
and the three target types function, member function and function object.
See the code in examples/basic_examples.cpp.
#include <functional>
#include <iostream>
#include <utility>
#include <rome/delegate.hpp>
void print(int i) {
std::cout << i << '\n';
}
int plus100(int i) {
return i + 100;
}
struct TargetClass {
int value = 0;
void set(int i) {
value = i;
}
};
struct Example {
rome::delegate<int(int), rome::target_is_mandatory> onMandatoryNotEmpty;
rome::delegate<int(int) /*, rome::target_is_expected*/> onExpectedNotEmpty; // (1)
// rome::delegate<int(int), rome::target_is_optional> onMaybeEmpty; // (2) does not compile
rome::delegate<void(int), rome::target_is_optional> onMaybeEmpty;
Example(decltype(onMandatoryNotEmpty)&& mand) : onMandatoryNotEmpty{std::move(mand)} { // (3)
}
};
int main() {
TargetClass obj{};
Example x{std::negate<>{}}; // (3)
std::cout << "Calls after initialization:\n";
print(x.onMandatoryNotEmpty(1));
try {
x.onExpectedNotEmpty(2);
}
catch (const rome::bad_delegate_call& e) {
std::cout << e.what() << '\n';
}
x.onMaybeEmpty(3);
std::cout << "\nCalls with fresh assigned targets:\n";
x.onMandatoryNotEmpty = [](int i) { return i + 10; };
x.onExpectedNotEmpty = [](int i) { return plus100(i); };
x.onMaybeEmpty = [&obj](int i) { obj.set(i); };
print(x.onMandatoryNotEmpty(4));
print(x.onExpectedNotEmpty(5));
print(obj.value);
x.onMaybeEmpty(6);
print(obj.value);
std::cout << "\nCalls after dropping targets:\n";
// x.onMandatoryNotEmpty = nullptr; // (4) does not compile
x.onExpectedNotEmpty = nullptr;
x.onMaybeEmpty = nullptr;
print(x.onMandatoryNotEmpty(7));
try {
x.onExpectedNotEmpty(8);
}
catch (const rome::bad_delegate_call& e) {
std::cout << e.what() << '\n';
}
x.onMaybeEmpty(9);
print(obj.value);
}
- (1) - second template parameter is
rome::target_is_expected
by default - (2) -
rome::delegate
withrome::target_is_optional
must have void return - (3) -
rome::delegate
withrome::target_is_mandatory
has deleted default constructor, a target must be assigned during construction - (4) -
rome::delegate
withrome::target_is_mandatory
does not allow to drop targets
Output:
Calls after initialization:
-1
rome::bad_delegate_callCalls with fresh assigned targets:
14
105
0
6Calls after dropping targets:
17
rome::bad_delegate_call
6
- rome::fwd_delegate
The same asrome::delegate
but restricts data to be forwarded only. - 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.