I need to take an array of input arguments of size n and bind its values to another function that takes n arguments.
I tried using bind to pass the elements of the array one by one to the function but it didn't work (kind of like using bind_front inside a loop).
What I need is something like:
#include <iostream>
#include <functional>
using namespace std;
int addThreeNumbers(int a, int b, int c)
{
return a+b+c;
}
int main()
{
int parameters[] = {1, 2, 3};
auto f = addThreeNumbers;
// Bind each element from the list one by one
for(int i=1; i<parametersLength; i++)
{
f = bind(f, parameters[i]);
}
f(); // call addThreeNumbers
return 0;
}
The solution would need to work with parameters being an array of any size. Is this possible to do using c++ 11?
I got it working thanks to Aconcagua and Philipp Lenk! Here is the working code:
#include <iostream>
#include <functional>
using namespace std;
// index sequence only
template <size_t ...>
struct indexSequence
{ };
template <size_t N, size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
{ };
template <size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
{ using type = indexSequence<Next ... >; };
template <size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;
int addThreeNumbers(int a, int b, int c)
{
return a+b+c;
}
template <typename F, typename T, size_t N, size_t ... I>
auto dispatch(F function, T(&array)[N], indexSequence<I ...>) -> decltype(function(array[I]...))
{
return function(array[I]...);
}
template <typename F, typename T, size_t N>
auto dispatch(F function, T(&array)[N]) -> decltype(dispatch(function, array, makeIndexSequence<N>()))
{
return dispatch(function, array, makeIndexSequence<N>());
}
int main()
{
int a[] = { 1, 2, 3 };
int s = dispatch(addThreeNumbers, a);
cout << s << endl;
return 0;
}
Got it working using tuples as well:
#include <iostream>
#include <tuple>
#include <string>
using namespace std;
// index sequence only
template <size_t ...>
struct indexSequence
{ };
template <size_t N, size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
{ };
template <size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
{ using type = indexSequence<Next ... >; };
template <size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;
template <class F, class Tuple, std::size_t... Is>
constexpr auto apply_impl(const F& f, Tuple t, indexSequence<Is...>) -> decltype(f(std::get<Is>(t)...))
{
return f(std::get<Is>(t)...);
}
template <class F, class Tuple>
constexpr auto apply(const F& f, Tuple t) -> decltype(apply_impl(f, t, makeIndexSequence<std::tuple_size<Tuple>{}>{}))
{
return apply_impl(f, t, makeIndexSequence<std::tuple_size<Tuple>{}>{});
}
int sum(int a, int b, string c)
{
cout << c << endl;
return a+b;
}
int main()
{
auto parameters = std::make_tuple(1,2,"1+2=3");
int s = apply(sum, parameters);
cout << s << endl;
return 0;
}
If I read your question right, you actually intend to decompose the array into single parameters. If so, you can do that with a std::index_sequence
:
template <typename F, typename T, size_t N, std::size_t ... I>
auto dispatch(F function, T(&array)[N], std::index_sequence<I ...>)
{
return function(array[I]...);
}
template <typename F, typename T, size_t N>
auto dispatch(F function, T(&array)[N])
{
return dispatch(function, array, std::make_index_sequence<N>());
}
Usage example:
int sum(int a, int b, int c)
{
return a + b + c;
}
int a[] = { 1, 2, 3 };
int s = dispatch(sum, a);
Of course, you'll get a compilation error if array length and number of function parameters do not match...
Edit: As std::index_sequence
is not available before C++14, you might implement your own variant of. This answer explains how you could do so.
Basically you seem to be looking for std::apply (or maybe a lambda wrapping a call to it + the arguments)
If your question concerned C++17 or newer and your parameters were stored in a std::array instead, the solution would look quite simple:
#include <array>
#include <iostream>
#include <tuple>
int addThreeNumbers(int a, int b, int c)
{
return a+b+c;
}
int main()
{
std::array parameters = {1, 2, 3};
auto f = [&]()
{
return std::apply(addThreeNumbers,parameters);
};
std::cout<<f();
return 0;
}
As you asked for C++11 your life is going to be ever so slightly more difficult, as std::apply is not yet part of the standard library. Something similar can, however, be implemented, the linked cppreference page even contains an implementation that can be used pretty much as shown: (significantly simplified for your case, using const references instead of proper perfect forwarding to be more concise and clear about the intentions and method used, without std::invoke(another C++17 addition) and only for array, to avoid tuple_size(which was not defined for std::array before C++17 either))
#include <iostream>
#include <tuple>
int addThreeNumbers(int a, int b, int c)
{
return a+b+c;
}
namespace detail
{
template <class F, class T, std::size_t N, std::size_t... I>
constexpr decltype(auto) apply_impl(const F& f, const std::array<T,N>& t, std::index_sequence<I...>)
{
return f(t[I]...);
}
}
template <class F, class T, std::size_t N>
constexpr decltype(auto) apply(const F& f, const std::array<T,N>& t)
{
return detail::apply_impl(f, t, std::make_index_sequence<N>{});
}
int main()
{
std::array<int,3> parameters= {1, 2, 3};
auto f = [&]()
{
return apply(addThreeNumbers,parameters);
};
std::cout<<f();
return 0;
}
Cool, we are down to C++14 now, yet trying to compile this as C++11 reveals yet another problem: std::index_sequence was a C++14 addition. Luckily for us this, too, was not a language addition and can be implemented for C++11(again, simplified and grossly inefficient version, the answer linked by Aconcagua contains a much better and detailed explanation):
template <std::size_t... I>
struct index_sequence{};
namespace detail
{
template <std::size_t N, std::size_t... I>
struct index_sequence_helper
{
using type=typename index_sequence_helper<N-1,N-1,I...>::type;
};
template <std::size_t... I>
struct index_sequence_helper<0,I...>
{
using type=index_sequence<I...>;
};
}
template <std::size_t N>
using make_index_sequence=typename detail::index_sequence_helper<N>::type;
With this in place, we finally have a complete and working C++11 solution for your original question(with some more minor adjustments, like missing return type deduction):
#include <array>
#include <iostream>
int addThreeNumbers(int a, int b, int c)
{
return a+b+c;
}
template <std::size_t... I>
struct index_sequence{};
namespace detail
{
template <std::size_t N, std::size_t... I>
struct index_sequence_helper
{
using type=typename index_sequence_helper<N-1,N-1,I...>::type;
};
template <std::size_t... I>
struct index_sequence_helper<0,I...>
{
using type=index_sequence<I...>;
};
}
template <std::size_t N>
using make_index_sequence=typename detail::index_sequence_helper<N>::type;
namespace detail {
template <class F, class T, std::size_t N, std::size_t... I>
constexpr auto apply_impl(const F& f, const std::array<T,N>& t, index_sequence<I...>) -> decltype(f(t[I]...))
{
return f(t[I]...);
}
}
template <class F, class T, std::size_t N>
constexpr auto apply(const F& f, const std::array<T,N>& t) -> decltype(detail::apply_impl(f, t, make_index_sequence<N>{}))
{
return detail::apply_impl(f, t, make_index_sequence<N>{});
}
int main()
{
std::array<int,3> parameters= {1, 2, 3};
auto f = [&]()
{
return apply(addThreeNumbers,parameters);
};
std::cout<<f();
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With