Class that is able to perform dynamic dispatch on multiple functions with two parameters. 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, 2 >::Type | UserData |
Addition parameter for user data, only useful if Signature contains more than 2 parameters. | |
Public Member Functions | |
DoubleDispatcher (bool symmetric=true) | |
Constructor. More... | |
DoubleDispatcher (DoubleDispatcher &&source) | |
Move constructor. | |
DoubleDispatcher & | operator= (DoubleDispatcher &&source) |
Move assignment operator. | |
~DoubleDispatcher () | |
Destructor. | |
template<typename Id1 , typename Id2 , typename Fn > | |
void | bind (const Id1 &identifier1, const Id2 &identifier2, Fn function) |
Registers a function bound to a specific key. More... | |
Result | call (Parameter arg1, Parameter arg2) const |
Dispatches the key of arg1 and arg2 and invokes the corresponding function. More... | |
Result | call (Parameter arg1, Parameter arg2, UserData data) const |
Invokes the function depending on arg1 and arg2 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 two parameters.
Sometimes you encounter the situation where you need to implement polymorphic behavior to dispatch dynamically on more than one type. Like overloading functions with two parameters at compile time, this class allows you to perform a dispatch on two arguments at runtime. At invocation time, all you need is the static type of the base class, the DoubleDispatcher figures out which dynamic types match which function.
Signature | Function signature R(B, B)or R(B, 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);
// trampoline2() takes a function that is passed to DoubleDispatcher::bind() and modifies it in order to fit the common
// R(B, 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 two template parameters Id1 and Id2 are required, as they will be explicitly specified when trampoline2() is called.
template <typename Id1, typename Id2, typename Fn>
static std::function<R(B, B)> trampoline2(Fn f);
// Optional function that returns a string representation of key for debugging.
static const char* name(Key k);
};
|
Usage example:
|
explicit |
Constructor.
symmetric | Is true if the calls fn(a,b) and fn(b,a) are equivalent and it's enough to register one of both variants. Otherwise, both calls have to be registered separately and are resolved to different functions. |
void aurora::DoubleDispatcher< Signature, Traits >::bind | ( | const Id1 & | identifier1, |
const Id2 & | identifier2, | ||
Fn | function | ||
) |
Registers a function bound to a specific key.
Id1,Id2 | Types that identify the argument types. By default, these are aurora::Type<D>, where D is a derived class. Can be deduced from the argument. |
Fn | Type of the function. Can be deduced from the argument. |
identifier1,identifier2 | Values that identify the object. The key, which is mapped to the function, is computed from each identifier through Traits::keyFromId(identifier). |
function | Function to register and associate with the given identifier. Usually, the function has the signature Result(Parameter, 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 third parameter for the Signature template parameter, the function should have the signature Result(Parameter, Parameter, UserData) . |
Result aurora::DoubleDispatcher< Signature, Traits >::call | ( | Parameter | arg1, |
Parameter | arg2 | ||
) | const |
Dispatches the key of arg1
and arg2
and invokes the corresponding function.
Traits::keyFromBase(arg)
is invoked to determine the key of each passed argument. The function bound to the combination of both keys 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.
When the dispatcher is configured in symmetric mode (see constructor), then the arguments are forwarded to the correct parameters in the registered functions, even if the order is different. When necessary, they are swapped. In other words, symmetric dispatchers don't care about the order of the arguments at all.
arg1,arg2 | Function arguments as references or pointers. |
FunctionCallException | when no corresponding function is found and no fallback has been registered. |
Result aurora::DoubleDispatcher< Signature, Traits >::call | ( | Parameter | arg1, |
Parameter | arg2, | ||
UserData | data | ||
) | const |
Invokes the function depending on arg1
and arg2
and passes a user-defined argument data
.
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.
When the dispatcher is configured in symmetric mode (see constructor), then the arguments are forwarded to the correct parameters in the registered functions, even if the order is different. When necessary, they are swapped. In other words, symmetric dispatchers don't care about the order of the first two arguments.
This method is only enabled if the Signature
template parameter contains 3 parameters.
arg1,arg2 | Function arguments as references or pointers. |
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::DoubleDispatcher< 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, 2>().
function | Function according to the specified signature. |