3.2. Compile-time dispatch

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);
}

3.2.1. Implementation details

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.