Suppose I have an std::function that takes as input N arguments of type T (this can be constructed using some metaprogramming magic; see below), where N is a template parameter. I would like to std::bind the first argument to construct a function with N-1 arguments (e.g. myBind<...>(someValue)). I could not think of a clever metaprogramming trick to do this. Any suggestions?
From Lambda function with number of arguments determined at compile-time:
You can write a template
n_ary_functionwith a nestedtypedeftype. This type can be used as follows:template <int N> class A { typename n_ary_function<N, double>::type func; };Following the definition of
n_ary_function:template <std::size_t N, typename Type, typename ...Types> struct n_ary_function { using type = typename n_ary_function<N - 1, Type, Type, Types...>::type; }; template <typename Type, typename ...Types> struct n_ary_function<0, Type, Types...> { using type = std::function<void(Types...)>; };
std::bind uses std::is_placeholder to detect placeholders, which means that you can write your own placeholders to use with std::bind by partially specializing std::is_placeholder:
template<int N>
struct my_placeholder { static my_placeholder ph; };
template<int N>
my_placeholder<N> my_placeholder<N>::ph;
namespace std {
template<int N>
struct is_placeholder<::my_placeholder<N>> : std::integral_constant<int, N> { };
}
This makes it possible to get a placeholder from an integer. The rest is simply the standard integer sequence trick:
template<class R, class T, class...Types, class U, int... indices>
std::function<R (Types...)> bind_first(std::function<R (T, Types...)> f, U val, std::integer_sequence<int, indices...> /*seq*/) {
return std::bind(f, val, my_placeholder<indices+1>::ph...);
}
template<class R, class T, class...Types, class U>
std::function<R (Types...)> bind_first(std::function<R (T, Types...)> f, U val) {
return bind_first(f, val, std::make_integer_sequence<int, sizeof...(Types)>());
}
Demo. std::integer_sequence is technically C++14, but it's easily implementable in C++11 - just search on SO.
#include <functional>
#include <cstddef>
#include <utility>
#include <tuple>
template <std::size_t N, typename Type, typename... Types>
struct n_ary_function
{
using type = typename n_ary_function<N - 1, Type, Type, Types...>::type;
};
template <typename Type, typename... Types>
struct n_ary_function<0, Type, Types...>
{
using type = std::function<void(Types...)>;
};
using placeholders_list = std::tuple<decltype(std::placeholders::_1)
, decltype(std::placeholders::_2)
, decltype(std::placeholders::_3)
, decltype(std::placeholders::_4)
, decltype(std::placeholders::_5)
, decltype(std::placeholders::_6)
, decltype(std::placeholders::_7)
, decltype(std::placeholders::_8)
, decltype(std::placeholders::_9)
, decltype(std::placeholders::_10)
>;
template <typename F>
struct arity;
template <typename R, typename... Args>
struct arity<std::function<R(Args...)>>
{
static constexpr std::size_t value = sizeof...(Args);
};
template <typename F, typename T, std::size_t... Ints>
auto binder(F f, T t, std::index_sequence<Ints...>)
{
return std::bind(f, t,
typename std::tuple_element<Ints, placeholders_list>::type{}...);
}
template <typename F, typename T>
auto myBind(F f, T t)
{
return binder(f, t, std::make_index_sequence<arity<F>::value - 1>{});
}
Tests:
#include <iostream>
void foo(int a, int b, int c, int d, int e)
{
std::cout << a << b << c << d << e << std::endl;
}
int main()
{
n_ary_function<5, int>::type f = foo;
n_ary_function<4, int>::type b = myBind(f, 1);
b(2, 3, 4, 5);
}
DEMO
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