Class that is able to perform dynamic dispatch on multiple functions with one parameter. More...
Public Types | |
typedef FunctionResult< Signature >::Type | Result |
Function return type. | |
typedef FunctionParam< Signature, 0 >::Type | Parameter |
Function parameter type denoting the object used for the dispatch. | |
typedef FunctionParam< Signature, 1 >::Type | UserData |
Addition parameter for user data, only useful if Signature contains more than 1 parameter. | |
Public Member Functions | |
SingleDispatcher () | |
Default constructor. | |
SingleDispatcher (SingleDispatcher &&source) | |
Move constructor. | |
SingleDispatcher & | operator= (SingleDispatcher &&source) |
Move assignment operator. | |
~SingleDispatcher () | |
Destructor. | |
template<typename Id , typename Fn > | |
void | bind (const Id &identifier, Fn function) |
Registers a function bound to a specific key. More... | |
Result | call (Parameter arg) const |
Dispatches the key of arg and invokes the corresponding function. More... | |
Result | call (Parameter arg, UserData data) const |
Invokes the function depending on arg and passes a user-defined argument data . More... | |
void | fallback (std::function< Signature > function) |
Registers a fallback function. More... | |
Class that is able to perform dynamic dispatch on multiple functions with one parameter.
Sometimes you encounter the situation where you need to implement polymorphic behavior, but you cannot or don't want to add a virtual function to an existing class hierarchy. Here comes dynamic dispatch into play: You define free functions, which can be treated by the dispatcher like virtual functions.
Signature | Function signature R(B) or R(B, U), with the following types:
|
Traits | Traits class to customize the usage of the dispatcher. To define your own traits, you can (but don't have to) inherit the class aurora::DispatchTraits<K>, where K is your key. It predefines most members for convenience. In general, the Traits class must contain the following members: struct Traits
{
// The type that is used to differentiate objects. For RTTI class hierarchies, std::type_index is a good choice
// -- but you're free to choose anything, such as an enum or a string. The requirements are that Key can be used
// as a key in std::unordered_map, i.e. it must support a std::hash<Key> specialization and operator==.
typedef K Key;
// A function that returns the corresponding key (such as std::type_index) from a type identifier (such as aurora::Type<T>).
// The type identifier is passed to bind() and can contain static type information, while the key is used by the map
// storing the registered functions. Often, key and type identifier are the same.
static Key keyFromId(Id id);
// Given a function argument base, this static function extracts the key from it. B corresponds to the template parameter
// specified at SingleDispatcher, that is, it is a reference or pointer.
static Key keyFromBase(B base);
// trampoline1() takes a function that is passed to SingleDispatcher::bind() and modifies it in order to fit the common
// R(B) signature. It therefore acts as a wrapper for user-defined functions which can link different signatures together.
// For example, this is the place to insert downcasts.
// The first template parameter Id is required, as it will be explicitly specified when trampoline1() is called.
template <typename Id, typename Fn>
static std::function<R(B)> trampoline1(Fn f);
// Optional function that returns a string representation of key for debugging.
static const char* name(Key k);
};
|
Usage example:
void aurora::SingleDispatcher< Signature, Traits >::bind | ( | const Id & | identifier, |
Fn | function | ||
) |
Registers a function bound to a specific key.
Id | Type that identifies the class. By default, this is aurora::Type<D>, where D is the derived class. Can be deduced from the argument. |
Fn | Type of the function. Can be deduced from the argument. |
identifier | Value that identifies the object. The key, which is mapped to the function, is computed from the identifier through Traits::keyFromId(identifier). |
function | Function to register and associate with the given identifier. Usually, the function has the signature Result(Parameter) , but it's possible to deviate from it (e.g. using derived classes), see also the note about trampolines in the Traits classes. In case you specified a second parameter for the Signature template parameter, the function should have the signature Result(Parameter, Parameter, UserData) . |
Result aurora::SingleDispatcher< Signature, Traits >::call | ( | Parameter | arg | ) | const |
Dispatches the key of arg
and invokes the corresponding function.
Traits::keyFromBase(arg)
is invoked to determine the key of the passed argument. The function bound to that key is then looked up in the map and invoked. If no match is found and a fallback function has been registered using fallback(), then the fallback function will be invoked.
arg | Function argument as a reference or pointer. |
FunctionCallException | when no corresponding function is found and no fallback has been registered. |
Result aurora::SingleDispatcher< Signature, Traits >::call | ( | Parameter | arg, |
UserData | data | ||
) | const |
Invokes the function depending on arg
and passes a user-defined argument data
.
Dispatches the key of arg
and invokes the corresponding function
Traits::keyFromBase(arg)
is invoked to determine the key of the passed argument. The function bound to that key is then looked up in the map and invoked. If no match is found and a fallback function has been registered using fallback(), then the fallback function will be invoked.
This method is only enabled if the Signature
template parameter contains 2 parameters.
arg | Function argument as a reference or pointer. |
data | An additional user argument that is forwarded to the function. |
FunctionCallException | when no corresponding function is found and no fallback has been registered. |
void aurora::SingleDispatcher< Signature, Traits >::fallback | ( | std::function< Signature > | function | ) |
Registers a fallback function.
The passed function will be invoked when call() doesn't find a registered function. It can be used when not finding a match does not represent an exceptional situation, but a common case.
If you want to perform no action, you can pass aurora::NoOp<R, 1>().
function | Function according to the specified signature. |