1

I can't figure out how to invoke a function with a variable number of arguments retrieved from an iterator. I have a simple example using std::plus to illustrate the problem:

#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>
#include <iostream>
#include <array>

namespace mpl = boost::mpl;

int main() {
    using OP = std::plus<mpl::_>;
    std::array<int, 2> args = {1, 2};

    // This works (passing each arg separately)
    auto n1 = mpl::apply<OP, int>::type()(args[0], args[1]);

    std::cout << "plus (1,2) = " << n1 << std::endl;

    // what I want is more like:
    // auto n2 = mpl::apply<OP, int>::type()(args);
    // or
    // auto n3 = mpl::apply<OP, int>::type()(args.begin(), args.end());
}

I can't figure out how to cross this compile-time/run-time boundary. Thanks for any pointers!

KentH
  • 1,204
  • 1
  • 14
  • 23
  • First, the size must be a compile time constant; then use `make_index_sequence` to get a pack of indices, finally do a pack expansion to get a list of elements. – T.C. Feb 15 '15 at 06:34
  • 1
    like [here](http://coliru.stacked-crooked.com/a/ffe4d3cc939b838e) or [here](http://coliru.stacked-crooked.com/a/9241b1132a909a50)? – Piotr Skotnicki Feb 15 '15 at 07:05
  • @piotr: Your solution is exactly what I was looking for. I have copied it to the answer section. Feel free to duplicate my answer & I'll accept it! – KentH Feb 15 '15 at 19:43

2 Answers2

1

The comment from @PiotrS in the question comments section holds the right answer. I have copied his answer below (and updated for C++14 -- which cleans it up a lot). @PiotrS answer above is for C++11 (which is what I had asked for).

#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>
#include <iostream> 
#include <array>
#include <utility>

namespace mpl = boost::mpl;

template <typename OP, typename T, typename Args, std::size_t... Is>
auto apply(Args&& args, std::index_sequence<Is...>)
{
    return typename mpl::apply<OP, T>::type()(std::get<Is>(std::forward<Args>(args))...);
}

template <typename OP, typename T, typename Args, std::size_t N = std::tuple_size<std::decay_t<Args>>{}>
auto apply(Args&& args)
{
    return apply<OP, T>(std::forward<Args>(args), std::make_index_sequence<N>{});
}

int main() {
    using OP = std::plus<mpl::_>;
    std::array<int, 2> args = {1, 2};

    auto n1 = mpl::apply<OP, int>::type()(args[0], args[1]);
    std::cout << "plus (1,2) = " << n1 << std::endl;

    auto n2 = apply<OP, int>(args);
    std::cout << "plus ([1,2]) = " << n2 << std::endl;
}

This solution utilizes perfect forwarding of arguments. The calling syntax is just what I wanted. And as a bonus, this solution works for any tuple-like argument type (one that specializes std::get & std::tuple_size).

Thanks to @PiotrS!

KentH
  • 1,204
  • 1
  • 14
  • 23
0

std::make_index_sequence is a wonderful helper meta-function for such purposes:

template <typename Func, typename T, size_t N, size_t... Inds>
int ApplyFuncHelper(std::array<T, N> const& arr, std::index_sequence<Inds...>)
{
 return Func()(arr[Inds]...);
}

template <typename Func, typename T, size_t N>
int ApplyFunc(std::array<T, N> const& arr)
{
 return ApplyFuncHelper<Func>(arr, std::make_index_sequence<N>());
}

Live Demo

Pradhan
  • 16,391
  • 3
  • 44
  • 59
  • `std::get(arr)` -> `T` and `N` seem redundant. And why did you hard-code the return type to be `int`? – Piotr Skotnicki Feb 15 '15 at 07:08
  • @PiotrS. Yes, it is. I started off with something like `typename ArrayLike` and try to use the `constexpr size()`. Ran into an issue there and took the easy way out :) Let me edit it out here. Thanks! – Pradhan Feb 15 '15 at 07:10