lundi 5 août 2019

C++ function that chooses template argument at runtime

This question might provide an answer to another question I posted recently but I feel that the two questions are sufficiently different (this being more general). Also I do realize there are a few questions out there asking for something similar, e.g.

but I could not find anything on how I could implement this dispatching using a function.

Setup:

#include <vector>
#include <iostream>

template <class T>
size_t length(size_t len) {
  std::vector<T> vec(len, 100);
  return vec.size();
}

size_t length(size_t type, size_t len) {
  switch(type) {
    case 0: return length<int>(len);
    case 1: return length<double>(len);
    default: throw std::runtime_error("error");
  }
}

int main () {

  std::cout << "length: " << length(0, 4) << "\n";
  std::cout << "length: " << length(1, 5) << "\n";

  return 0;
}

I would like to write a function dispatch(type, fun, ...) that implements this switch like

auto dispatch(type, fun, ...) -> decltype(fun<int>(...)) {
  switch(type) {
    case 0: return fun<int>(...);
    case 1: return fun<double>(...);
    default: throw std::runtime_error("error");
  }
}

So far, I was able to come up with two ways of solving this:

Functor approach:

template <template<typename> class Func, typename ...Ar>
auto dispatch_type(size_t type, Ar&&... rg) ->
    decltype(Func<int>()(std::forward<Ar>(rg)...)) {
  switch(type) {
    case 0: return Func<int>()(std::forward<Ar>(rg)...);
    case 1: return Func<double>()(std::forward<Ar>(rg)...);
    default: throw std::runtime_error("error");
  }
}

template <class T>
struct Length {
  size_t operator()(size_t len) {
    std::vector<T> vec(len, 100);
    return vec.size();
  }
};

size_t length(size_t type, size_t len) {
  return dispatch_type<Length>(type, len);
}

Using boost::mp11:

#include <boost/mp11/list.hpp>
#include <boost/mp11/algorithm.hpp>

namespace mp11 = boost::mp11;

using num_types = mp11::mp_list<int, double>;
template <size_t i>
using num_type = mp11::mp_at_c<num_types, i>

template<class F>
inline constexpr
    decltype(std::declval<F>()(std::declval<mp11::mp_size_t<0>>()))
    with_type(std::size_t i, F && f) {

  return mp11::mp_with_index< mp11::mp_size<num_types> >(i,
      std::forward<F>(f));
}

size_t length(size_t i, size_t len) {
  return with_type(i, [&](auto I) {
    std::vector< num_type<I> > vec(len, 100);
    return vec.size();
  });
}

I am surprised this is so hard to achieve. Conceptually my problem is simple: Given a function template, make sure there exists explicit instantiations for a set of types (known at compile time) and dispatch the appropriate one at run time based on a switch.

What other options in addition to the two proposed ones exists? For my application I'm restricted to C++11, but within the context of this question, < C++17 is fine.

Aucun commentaire:

Enregistrer un commentaire