vendredi 15 juin 2018

Template pack expansion to apply a function to consecutive pairs of parameters

I am trying to create a variadic template function which would call a function to consecutive pairs of arguments.

The desired function signature would be:

template <typename ...Ts>
void apply(Ts &...args);

When called with apply(t1, t2, t3) the function should make a sequence of calls func(t1, t2)and func(t2, t3), where func is a function with signature:

template <typename L, typename R>
void func(L &left, R &right);

The order of operations is not really relevant in my context. The function has to be able to modify objects left and right, hence passed by reference. I cannot simply use polymorphic access through a base class pointer, since the objects have different class templates, a shared class cannot really be taken out.

Is it possible to achieve such a sequence of calls via a variadic template function? None of the pack expansion and fold expression examples that I've seen seem to cover such a scenario. Or should I pass my objects in a different fashion?

My initial attempt, included below (with some details omitted), packed all template parameters into a tuple, and then used a ‘const for-loop’ to ‘loop’ through the tuple elements. However, I soon came to realize that this approach would not work, because the lambda in the const-for loop invokes operator() const and therefore cannot modify the passed objects.

The code I was using does make the desired sequence of calls, but the objects are not modified (set_something() is not a const function). I had to resort to using a wrapper function with different numbers of template parameters, and making the calls to func manually.

template <std::size_t Begin, typename Callable, std::size_t... I>
constexpr void const_for_impl(Callable &&func, std::index_sequence<I...>) {
  (func(std::integral_constant<std::size_t, Begin + I>{}), ...);
}

template <std::size_t Begin, std::size_t End, typename Callable>
constexpr void const_for(Callable &&func) {
  const_for_impl<Begin>(std::forward<Callable>(func),
                        std::make_index_sequence<End - Begin>{});
};

template <typename... Ts>
void apply(Ts *... args) {
  auto tuple = std::make_tuple(std::forward<Ts>(args)...);

  const_for<0, sizeof...(args) - 1>(
      [&](auto I) { func((std::get<I>(tuple)), (std::get<I + 1>(tuple))); });
};

template <typename L, typename R>
void func(L &l, R &r) {
  // Validate with some type traits
  static_assert(has_some_property<L>::value);
  static_assert(has_another_property<R>::value);

  // Get a shared pointer to something common
  auto common = std::make_shared<typename something_common<L, R>::type>();

  l.set_something(common);
  r.set_something(common);
};

// Application scenario
int main() {

  ComplexObjectA<SomeType, SomeParameter> a;
  ComplexObjectB<AnotherType, AnotherParameter> b;
  ComplexObjectC c;

  apply(a, b, c);

  return 0;
}

Aucun commentaire:

Enregistrer un commentaire