Let us assume we want to implement an operation
Operation by means of three backend classes
A_impl, B_impl, and
C_impl, all modeling the same concept:
class A_impl
{
static void process(int*, size_t);
};
template <typename T>
class B_impl
{
static void process(T*, size_t);
};
template <typename T>
class C_impl
{
static void process(T*, size_t);
};For each backend, we define an Evaluator, that is, a meta-function that will be used to evaluate whether for a given type this backend is usable.
Then we expose these backends by declaring a list of backends for the operation in question, so the dispatcher can iterate over it to find the first match. (Note that typically this list will also depend on configuration parameters, so the selection of the appropriate backend is in fact done in part at configure time and in part at compile time.
#include <vsip/opt/dispatch.hpp>
struct Operation;
struct A;
struct B;
struct C;
namespace vsip_csl
{
namespace dispatcher
{
template <>
struct Evaluator<Operation, A, int>
{
static bool const ct_valid = true;
typedef A_impl backend;
};
template <typename T>
struct Evaluator<Operation, B, T>
{
static bool const ct_valid = has_feature<T>::value;
typedef B_impl<T> backend;
};
template <typename T>
struct Evaluator<Operation, C, T>
{
static bool const ct_valid = true;
typedef C_impl<T> backend;
};
template <>
struct List<Operation>
{
typedef Make_type_list<A, B, C>::type type;
};
}
}Here, the Evaluator declares A to be available for int types, B is defined in a way that allows some external meta-function has_feature to evaluate the availability of this backend for a given type, while Evaluator C declares C to be available for all types. (Typically, a catch-all backend is made available that is guaranteed to work for all types, but as it is slow, it is only available if no other backend matches.)
With that, writing the wrapper function that will do the actual dispatch is very simple:
template <typename T>
void process(T *input, size_t size)
{
// Evaluate the dispatch (at compile-time):
using vsip_csl::dispatcher::Dispatcher<Operation, T>;
typedef Dispatcher<Operation, T>::backend backend_type;
// Now use it.
backend_type::process(input, size);
}The List<Operation> type above
creates a type-list of backend tags. The
Dispatcher<Operation> class template
constructs a type-list of Evaluators from that, which the compiler
iterates over to select the first item for which the
ct_valid member is true.
In other words, the
Dispatcher<Operation> acts as a
meta-function that takes a typename T as input, and returns
a backend.