Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to metaprogram a generic list extraction for building a function call

I have a family of classes with methods with the following signature:

double compute(list<T> pars)

This method performs a calculation with the parameters received through pars. For each compute(list) method, I have another compute(x1, x2, ..., xn) which is the method implementing the real calculation. Thus, compute(pars) should do some such as:

double compute(list<T> pars)
{
  T x1 = list.pop_back();
  T x2 = list.pop_back();
  // .. so on until last parameter xn
  T xn = list.pop_back();

  return compute(x1, x2, .., xn); // here the real implementation is called
}

This pattern repeats many times, the only thing that could change is the size of pars list and of course the implementation of compute(x1, x1, ..).

I would like to find a way for "driying" this repetitive process; concretely, extracting the parameters in pars list and building the call to compute(x1, x2, .., xn). I have been trying without success to do some macro tricks.

My question is if it exists some way based on metaprogramming that allows me to implement compute(list<T> pars) once and simply reuse it n order to perform the call to compute(x1, x2, ..., xn)

EDIT: This is the signature of the other compute(x1, ...)

VtlQuantity compute(const VtlQuantity & x1, 
                    const VtlQuantity & x2,
                    // any number of pars according the class
                    const VtlQuantity & xn) const

'VtlQuantityis a class representingdouble`'s, their units and other stuff.

like image 592
lrleon Avatar asked Aug 26 '16 12:08

lrleon


1 Answers

You may do the following:

template <typename Func, typename T, std::size_t ... Is>
decltype(auto) apply(Func&& f, const std::list<T>& pars, std::index_sequence<Is...>)
{
    std::vector<T> v(pars.rbegin(), pars.rend());

    return std::forward<Func>(f)(v.at(Is)...);
}

template <std::size_t N, typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
    return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}

With usage similar to:

apply<6>(print, l);

Demo

To compute automatically the arity of the function, you may create a traits:

template <typename F> struct arity;

template <typename Ret, typename ...Args> struct arity<Ret(Args...)>
{
    static constexpr std::size_t value = sizeof...(Args);
};

and then

template <typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
    constexpr std::size_t N = arity<std::remove_pointer_t<std::decay_t<Func>>>::value;
    return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}

Demo

You have to enrich arity to support Functor (as the lambda).

like image 118
Jarod42 Avatar answered Nov 01 '22 17:11

Jarod42