Cannot remove unwanted overloads - c++

The function transform conducted by
const std::vector<int> a = {1, 2, 3, 4, 5};
const std::vector<double> b = {1.2, 4.5, 0.6};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
std::vector<double> result(5);
transform<Foo> (result.begin(),
a.begin(), a.end(),
b.begin(), b.end(),
c.begin(), c.end());
is to carry out a generalization of std::transform on multiple containers, outputing the results in the vector result. One function with signature (int, double, const std::string&) would apparently be needed to handle the three containers in this example. But because the containers have different lengths, we instead need to use some overloads. I will test this using these member overloads of a holder class Foo:
static int execute (int i, double d, const std::string& s) {return i + d + s.length();}
static int execute (int i, const std::string& s) {return 2 * i + s.length();}
static int execute (int i) {return 3 * i - 1;}
However, the program will not compile unless I define three other overloads that are never even called, namely with arguments (int, double), (const std::string&) and (). I want to remove these overloads, but the program won't let me. You can imagine the problem this would cause if we had more than 3 containers (of different lengths), forcing overloads with many permutations of arguments to be defined when they are not even being used.
Here is my working program that will apparently show why these extraneous overloads are needed. I don't see how or why the are forced to be defined, and want to remove them. Why must they be there, and how to remove the need for them?
#include <iostream>
#include <utility>
#include <tuple>
bool allTrue (bool a) {return a;}
template <typename... B>
bool allTrue (bool a, B... b) {return a && allTrue(b...);}
template <typename F, size_t... Js, typename Tuple>
typename F::return_type screenArguments (std::index_sequence<>, std::index_sequence<Js...>, Tuple& tuple) {
return F::execute (*std::get<Js>(tuple)++...);
}
// Thanks to Barry for coming up with screenArguments.
template <typename F, std::size_t I, size_t... Is, size_t... Js, typename Tuple>
typename F::return_type screenArguments (std::index_sequence<I, Is...>, std::index_sequence<Js...>, Tuple& tuple) {
if (std::get<2*I>(tuple) != std::get<2*I+1>(tuple))
return screenArguments<F> (std::index_sequence<Is...>{}, std::index_sequence<Js..., 2*I>{}, tuple);
else
return screenArguments<F> (std::index_sequence<Is...>{}, std::index_sequence<Js...>{}, tuple);
}
template <typename F, typename Tuple>
typename F::return_type passCertainArguments (Tuple& tuple) {
return screenArguments<F> (std::make_index_sequence<std::tuple_size<Tuple>::value / 2>{},
std::index_sequence<>{}, tuple);
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator result, const std::index_sequence<Is...>&, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...);
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = passCertainArguments<F>(tuple);
return result;
}
template <typename F, typename OutputIterator, typename... InputIterators>
OutputIterator transform (OutputIterator result, InputIterators... iterators) {
return transformHelper<F> (result, std::make_index_sequence<sizeof...(InputIterators) / 2>{}, iterators...);
}
// Testing
#include <vector>
struct Foo {
using return_type = int;
static int execute (int i, double d, const std::string& s) {return i + d + s.length();}
static int execute (int i, const std::string& s) {return 2 * i + s.length();}
static int execute (int i) {return 3 * i - 1;}
// These overloads are never called, but apparently must still be defined.
static int execute () {std::cout << "Oveload4 called.\n"; return 0;}
static int execute (int i, double d) {std::cout << "Oveload5 called.\n"; return i + d;}
static int execute (const std::string& s) {std::cout << "Oveload6 called.\n"; return s.length();}
};
int main() {
const std::vector<int> a = {1, 2, 3, 4, 5};
const std::vector<double> b = {1.2, 4.5, 0.6};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
std::vector<double> result(5);
transform<Foo> (result.begin(),
a.begin(), a.end(),
b.begin(), b.end(),
c.begin(), c.end());
for (double x : result) std::cout << x << ' '; std::cout << '\n';
// 4 11 8 11 14 (correct output)
}

The compiler does not know at compile time which combinations of functions will be used in runtime. So you have to implement all the 2^N functions for every combination. Also your approach will not work when you have containers with the same types.
If you want to stick with templates, my idea is to implement the function something like this:
template <bool Arg1, bool Arg2, bool Arg3>
static int execute (int *i, double *d, const std::string *s);
The template arguments Arg1, Arg2, Arg3 represent the validity of each parameter. The compiler will automatically generate all the 2^N implementations for every parameter combination. Feel free to use if statements inside this function instead of template specialization - they will be resolved at compile time to if (true) or if (false).

I think I got! Template the function Foo::execute according to the number of arguments that are actually needed, and let them all have the same arguments:
struct Foo {
using return_type = int;
template <std::size_t> static return_type execute (int, double, const std::string&);
};
template <> Foo::return_type Foo::execute<3> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<2> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<1> (int i, double, const std::string&) {return 3 * i - 1;}
template <> Foo::return_type Foo::execute<0> (int, double, const std::string&) {return 0;} // The only redundant specialization that needs to be defined.
Here is the full solution.
#include <iostream>
#include <utility>
#include <tuple>
#include <iterator>
bool allTrue (bool b) {return b;}
template <typename... Bs>
bool allTrue (bool b, Bs... bs) {return b && allTrue(bs...);}
template <typename F, std::size_t N, typename Tuple, typename... Args>
typename F::return_type countArgumentsNeededAndExecute (Tuple&, const std::index_sequence<>&, Args&&... args) {
return F::template execute<N>(std::forward<Args>(args)...);
}
template <typename F, std::size_t N, typename Tuple, std::size_t I, size_t... Is, typename... Args>
typename F::return_type countArgumentsNeededAndExecute (Tuple& tuple, const std::index_sequence<I, Is...>&, Args&&... args) { // Pass tuple by reference, because its iterator elements will be modified (by being incremented).
return (std::get<2*I>(tuple) != std::get<2*I + 1>(tuple)) ?
countArgumentsNeededAndExecute<F, N+1> (tuple, std::index_sequence<Is...>{}, std::forward<Args>(args)..., // The number of arguments to be used increases by 1.
*std::get<2*I>(tuple)++) : // Pass the value that will be used and increment the iterator.
countArgumentsNeededAndExecute<F, N> (tuple, std::index_sequence<Is...>{}, std::forward<Args>(args)...,
typename std::iterator_traits<typename std::tuple_element<2*I, Tuple>::type>::value_type{}); // Pass the default value (it will be ignored anyway), and don't increment the iterator. Hence, the number of arguments to be used does not change.
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator result, const std::index_sequence<Is...>& indices, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...); // Cannot be const, as the iterators are being incremented.
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = countArgumentsNeededAndExecute<F, 0> (tuple, indices); // Start the count at 0. Examine 'indices', causing the count to increase one by one.
return result;
}
template <typename F, typename OutputIterator, typename... InputIterators>
OutputIterator transform (OutputIterator result, InputIterators... iterators) {
return transformHelper<F> (result, std::make_index_sequence<sizeof...(InputIterators) / 2>{}, iterators...);
}
// Testing
#include <vector>
struct Foo {
using return_type = int;
template <std::size_t> static return_type execute (int, double, const std::string&);
};
// Template the function Foo::execute according to the number of arguments that are actually needed:
template <> Foo::return_type Foo::execute<3> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<2> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<1> (int i, double, const std::string&) {return 3 * i - 1;}
template <> Foo::return_type Foo::execute<0> (int, double, const std::string&) {return 0;} // The only redundant specialization that needs to be defined.
int main() {
const std::vector<int> a = {1, 2, 3, 4, 5};
const std::vector<double> b = {1.2, 4.5, 0.6};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
std::vector<double> result(5);
transform<Foo> (result.begin(),
a.begin(), a.end(),
b.begin(), b.end(),
c.begin(), c.end());
for (double x : result) std::cout << x << ' '; std::cout << '\n';
// 4 11 8 11 14 (correct output)
}
And a second solution using Andrey Nasonov's bool templates to generate all 2^N overloads needed. Note that the above solution requires only N+1 template instantations for the overloads though.
#include <iostream>
#include <utility>
#include <tuple>
bool allTrue (bool b) {return b;}
template <typename... Bs>
bool allTrue (bool b, Bs... bs) {return b && allTrue(bs...);}
template <bool...> struct BoolPack {};
template <typename F, typename Tuple, bool... Bs, typename... Args>
typename F::return_type checkArgumentsAndExecute (const Tuple&, const std::index_sequence<>&, BoolPack<Bs...>, Args&&... args) {
return F::template execute<Bs...>(std::forward<Args>(args)...);
}
template <typename F, typename Tuple, std::size_t I, size_t... Is, bool... Bs, typename... Args>
typename F::return_type checkArgumentsAndExecute (Tuple& tuple, const std::index_sequence<I, Is...>&, BoolPack<Bs...>, Args&&... args) { // Pass tuple by reference, because its iterators elements will be modified (by being incremented).
return (std::get<2*I>(tuple) != std::get<2*I+1>(tuple)) ?
checkArgumentsAndExecute<F> (tuple, std::index_sequence<Is...>{}, BoolPack<Bs..., true>{}, std::forward<Args>(args)...,
*std::get<2*I>(tuple)++) : // Pass the value that will be used and increment the iterator.
checkArgumentsAndExecute<F> (tuple, std::index_sequence<Is...>{}, BoolPack<Bs..., false>{}, std::forward<Args>(args)...,
typename std::iterator_traits<typename std::tuple_element<2*I, Tuple>::type>::value_type{}); // Pass the default value (it will be ignored anyway), and don't increment the iterator.
}
template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator& result, const std::index_sequence<Is...>& indices, InputIterators... iterators) {
auto tuple = std::make_tuple(iterators...); // Cannot be const, as the iterators are being incremented.
while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
*result++ = checkArgumentsAndExecute<F> (tuple, indices, BoolPack<>{});
return result;
}
template <typename F, typename OutputIterator, typename... InputIterators>
OutputIterator transform (OutputIterator result, InputIterators... iterators) {
return transformHelper<F> (result, std::make_index_sequence<sizeof...(InputIterators) / 2>{}, iterators...);
}
// Testing
#include <vector>
struct Foo {
using return_type = int;
template <bool B1, bool B2, bool B3> static return_type execute (int, double, const std::string&) {return 0;} // All necessary overloads defined at once here.
};
// Specializations of Foo::execute<B1,B2,B3>(int, double, const std::string&) that will actually be called by transform<Foo> (it is the client's responsibility to define these overloads based on the containers passed to transform<Foo>).
template <> Foo::return_type Foo::execute<true, true, true> (int i, double d, const std::string& s) {return i + d + s.length();}
template <> Foo::return_type Foo::execute<true, false, true> (int i, double, const std::string& s) {return 2 * i + s.length();}
template <> Foo::return_type Foo::execute<true, false, false> (int i, double, const std::string&) {return 3 * i - 1;}
int main() {
const std::vector<int> a = {1, 2, 3, 4, 5};
const std::vector<double> b = {1.2, 4.5, 0.6};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
std::vector<double> result(5);
transform<Foo> (result.begin(),
a.begin(), a.end(),
b.begin(), b.end(),
c.begin(), c.end());
for (double x : result) std::cout << x << ' '; std::cout << '\n';
// 4 11 8 11 14 (correct output)
}

Related

Improving fold function

I have implemented a simple fold function in C++ that accepts a lambda, and can fold multiple vectors at the same time at compile time. I am wondering if it could be simplified in some manner (I have provided both a recursive version and an iteratively recursive version - I am unsure which should have better performance): https://godbolt.org/z/39pW81
Performance optimizations are also welcome - in that regard is any of the two approaches faster?
template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail, typename Function>
auto foldHelperR(Function&& func, const type_identity& id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
if constexpr (I>0)
{
return func(foldHelperR<I-1>(std::forward<Function>(func), id, head, tail...), head[I], tail[I]...);
}
else
{
return func(id, head[0], tail[0]...);
}
}
template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail, typename Function>
auto foldHelperI(Function&& func, const type_identity id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
if constexpr (I<N-1)
{
return foldHelperI<I+1>(std::forward<Function>(func), func(id, head[I], tail[I]...), head, tail...);
}
else
{
return func(id, head[N-1], tail[N-1]...);
}
}
template<typename type_identity, typename type_head, int N_head, typename ...type_tail, int ...N_tail, typename Function = void (const type_identity&, const type_head&, const type_tail&...)>
constexpr auto fold(Function&& func, const type_identity& id, const tvecn<type_head, N_head>& head, const tvecn<type_tail, N_tail>&... tail)
{
static_assert(std::is_invocable_v<Function, const type_identity&, const type_head&, const type_tail &...>,
"The function cannot be invoked with these zip arguments (possibly wrong argument count).");
static_assert(all_equal_v<N_head, N_tail...>, "Vector sizes must match.");
//return foldHelperR<N_head-1>(std::forward<Function>(func), id, head, tail...);
return foldHelperI<0>(std::forward<Function>(func), id, head, tail...);
}
int main()
{
tvecn<int,3> a(1,2,3);
return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);
}
and can fold multiple vectors at the same time at compile time
Not exactly: if you want to operate compile-time
(1) you have to define constexpr the tvecn constructor and
(2) you have to define constexpr the foldhelper function and
(3) you have to declare constexpr a
// VVVVVVVVV
constexpr tvecn<int,3> a(1,2,3);
(4) you have to place the result of fold in a constexpr variable (or, more generally speaking, in a place where the value is required compile time, as the size field of a C-style array, or a template value parameter, or a static_assert() test)
constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
0, a, a);
I am wondering if it could be simplified in some manner
Sure.
First of all: if you can, avoid to reinventing the weel: your tvecn is a simplified version of std::array.
Suggestion: use std::array (if you can obviously)
Second: you tagged C++17 so you can use folding
Suggestion: use it also for all_equal
template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
{ };
template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;
More in general: when you have to define a custom type traits that has to provide a number, inherit (if possible) from std::integral_constant (or std::bool_constant, or std::true_type, or std::false_type: all std::integral_constant specializations). So you automatically inherit all std::integral_constant facilities.
Third: almost all C++ standard uses std::size_t, not int, for sizes.
Suggestion: when you have to do with sizes, use std::size_t, not int. This way you can avoid a lot of annoying troubles.
Fourth: from main() you should return only EXIT_SUCCESS (usually zero) or EXIT_FAILURE (usually 1)
Suggestion: avoid things as
return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);
Fifth: never underestimate the power of the comma operator.
Suggestion: avoid recursion at all and use template folding also for the helper function; by example
template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
F const & f, T id, As const & ... arrs)
{ return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }
that you can call as follows from fold()
return foldHelperF(std::make_index_sequence<N_head>{},
std::forward<Function>(func),
id, head, tail...);
The following is a full compiling, and simplified, example
#include <array>
#include <utility>
#include <iostream>
#include <type_traits>
template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
{ };
template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;
template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
F const & f, T id, As const & ... arrs)
{ return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }
template <typename type_identity, typename type_head, std::size_t N_head,
typename ...type_tail, std::size_t ...N_tail,
typename Function = void (type_identity const &,
type_head const &,
type_tail const & ...)>
constexpr auto fold (Function && func, type_identity const & id,
std::array<type_head, N_head> const & head,
std::array<type_tail, N_tail> const & ... tail)
{
static_assert( std::is_invocable_v<Function, const type_identity&,
const type_head&, const type_tail &...>,
"The function cannot be invoked with these zip arguments"
" (possibly wrong argument count).");
static_assert( all_equal_v<N_head, N_tail...>,
"Vector sizes must match.");
return foldHelperF(std::make_index_sequence<N_head>{},
std::forward<Function>(func),
id, head, tail...);
}
int main()
{
constexpr std::array<int, 3u> b{2, 5, 7};
constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
0, b, b);
std::cout << f << std::endl;
}
With Fold expression, it might be:
template <typename F, typename Init, std::size_t... Is, typename... Arrays>
constexpr auto fold_impl(F&& f, Init init, std::index_sequence<Is...>, Arrays&&... arrays)
{
auto l = [&](Init init, std::size_t i){ return f(init, arrays[i]...); };
return ((init = l(init, Is)), ...);
}
template <typename F, typename Init, typename Array, typename ... Arrays>
constexpr auto fold(F&& f, Init init, Array&& array, Arrays&&... arrays)
{
static_assert(((arrays.size() == array.size()) && ...));
return fold_impl(f, init, std::make_index_sequence<array.size()>{}, array, arrays...);
}
Demo

template function that deduces both containers and initializer_list-s

I'd like to write a helper function like:
template <typename F, typename Range1, typename Range2>
auto helper(const Range1& left, const Range2& right, F&& pred)
{
using namespace std; // for cbegin/cend and ADL
return pred(cbegin(left), cend(left), cbegin(right), cend(right));
}
It works well for containers:
std::vector<int> v1 = {1,2,3,4,5,6};
std::vector<int> v2 = {5,4,3,2,1,6};
std::cout << helper(v1, v2, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
but it fails to deduce initializer_list-s (example):
std::cout << helper({1,2,3,4,5,6}, {5,4,3,2,1,6}, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
Is there an idiomatic way to rewrite helper so that it deduces both containers and initializer_list-s?
I can't come up with anything better than overloads for all combinations of container and initializer_list.
I think the fundamental problem here is that a braced-init-list like { 1, 2, 3 } is just an initializer and not an object of type std::initializer_list<T>. It can potentially be used to initialize an object of some given type. But it's not an object of any type itself. And there doesn't seem to be anything in the rules for function template argument deduction that would allow you to get an std::initializer_list<T> from a braced-init-list argument unless your function parameter was already declared to be some sort of std::initializer_list<T> to begin with.
So I'm afraid writing those overloads will be the simplest solution…
Here is the best I can do:
template<class X>
struct Range {
X* container;
Range(X& x):container(std::addressof(x)) {}
Range(X&& x):container(std::addressof(x)) {} // dangerous, but hey
auto begin() const { using std::begin; return begin(*container); }
auto end() const { using std::end; return end(*container); }
auto cbegin() const { using std::cbegin; return cbegin(*container); }
auto cend() const { using std::cend; return cend(*container); }
};
template<class T>
struct Range<std::initializer_list<T>> {
using X=std::initializer_list<T>;
X container;
Range(X x):container(x) {}
auto begin() const { using std::begin; return begin(container); }
auto end() const { using std::end; return end(container); }
auto cbegin() const { using std::cbegin; return cbegin(container); }
auto cend() const { using std::cend; return cend(container); }
};
template<class T>
Range( std::initializer_list<T> ) -> Range<std::initializer_list< T >>;
template<class C1, class C2>
void foo( Range<C1> r1, Range<C2> c2 ) {}
test code:
Range r = {{'a', 'b', 'c'}};
(void)r;
std::vector v = {1,2,3};
foo( Range{{'a','b','c'}}, Range{v} );
you have to cast the arguments to Range manually for this to work at the call site, because class template arguement deduction doesn't work on function arguments.
We might be able to attack it differently.
template <typename F, typename Range1, typename Range2>
auto helper(const Range1& left, const Range2& right, F&& pred)
change the above syntax to a chained-call.
helper(v1)({1,2,3})[pred];
that reduces the 2^n explosion into 2. Not much of a help with 2 overloads, but still...
template<class...Ts>
struct helper_t {
std::tuple<Ts&&...> containers;
template<class T>
helper_t<T, Ts...> operator()(T&& t)&& {
return { /* append-move containers and t into one tuple */ };
}
template<class T>
helper_t<std::initializer_list<T>, Ts...> operator()(std::initializer_list<T> t)&& {
return { /* append-move containers and t into one tuple */ };
}
template<class F>
decltype(auto) operator[](F&& f)&& {
return std::move(*this).apply_impl(
std::make_index_sequence<sizeof...(Ts)>{},
std::forward<F>(f)
);
}
private:
template<std::size_t...Is, class F>
decltype(auto) apply_impl( std::index_sequence<Is...>, F&& f ) && {
using std::cbegin; using std::cend;
using std::get;
return std::forward<F>(f)(
cbegin( get<Is>(std::move(containers)) ), cend( get<Is>(std::move(containers)) )
);
}
};
static constexpr const helper_t<> helper;
I left appending the tuples as an exercise.
helper( container1 )( {1,2,3} )( container2 )[ some_lambda ];
is the syntax.
Please explicitly specify that the arguments you are passing is the initializer_list like this:-
std::cout << helper(v1, std::initializer_list<int>{5,4,3,2,1,6}, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
std::cout << helper(std::initializer_list<int>{1,2,3,4,5,6}, std::initializer_list<int>{5,4,3,2,1,6}, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
This is the way you can pass the initializer_list else you need to overload for all combinations of container and initializer_list
As explained by Michael Kenzel, the problem is that a braced-init-list isn't an std::intializer_list.
So I'm agree with Michael (+1): I don't see a way to write a single template function that can deduce both STL containers and (as std::initilizer_list<T>) braced-init-lists.
But if you can accept to add an helper() helper function for your helper() function, you can use the fact that a braced-init-list can be deduced as a C-style array.
So you can write a helper() specific version for C-style arrays that can convert the C-style arrays in std::initializer_list (or std::array, or... see you) or, in my example, you can simply call your original helper() function passing the arrays but explicating the types.
I mean: you can add this helper() helper function
template <typename F, typename R1, std::size_t S1,
typename R2, std::size_t S2>
auto helper (R1 const (&r1)[S1], R2 const (&r2)[S2], F && pred)
{ return helper<F, R1[S1], R2[S2]>(r1, r2, std::forward<F>(pred)); }
Drawback: this works if both ranges are STL containers or if both ranges are C-style arrays (or braced-init-lists); if you can have a STL container and a C-style array, you have to write other two helper() helper function: one for (only) the first range and one for (only) the second.
The following is a full working example
#include <vector>
#include <iostream>
#include <algorithm>
template <typename F, typename Range1, typename Range2>
auto helper (Range1 const & left, Range2 const & right, F && pred)
{ return pred(std::cbegin(left), std::cend(left),
std::cbegin(right), std::cend(right)); }
template <typename F, typename R1, std::size_t S1,
typename R2, std::size_t S2>
auto helper (R1 const (&r1)[S1], R2 const (&r2)[S2], F && pred)
{ return helper<F, R1[S1], R2[S2]>(r1, r2, std::forward<F>(pred)); }
int main ()
{
std::vector<int> v1 = {1,2,3,4,5,6};
std::vector<int> v2 = {5,4,3,2,1,6};
std::cout << helper(v1, v2,
[](auto const & ... args){ return std::is_permutation(args...);})
<< std::endl;
std::cout << helper({1, 2, 3, 4, 5, 6}, {5, 4, 3, 2, 1, 6},
[](auto const &... args){ return std::is_permutation(args...);})
<< std::endl;
}

Functional composition with variadic templates

My goal is to get composition of functions working with this exact syntax:
int main() {
Function<std::string, int> f([](const std::string& s) {return s.length();});
Function<int, double> g([](int x) {return x + 0.5;});
Function<double, int> h([](double d) {return int(d+1);});
std::cout << compose(g, f, "hello") << '\n'; // g(f("hello")) = 5.5
std::cout << compose(h, g, f, "hello") << '\n'; // h(g(f("hello"))) = 6
}
By changing the syntax slightly so that the "hello" argument goes first, I have it working easily with the following code:
#include <iostream>
#include <functional>
#include <tuple>
#include <string>
template <typename D, typename R>
struct Function {
using domain = const D&;
using range = R;
using function = std::function<range(domain)>;
const function& f;
Function (const function& f) : f(f) {}
range operator()(domain x) const {return f(x);}
};
template <typename... Ts>
struct LastType {
using Tuple = std::tuple<Ts...>;
using type = typename std::tuple_element<std::tuple_size<Tuple>::value - 1, Tuple>::type;
};
template <typename F, typename G>
typename G::range compose (const typename F::domain& x, const G& g, const F& f) {
return g(f(x));
}
template <typename F, typename... Rest>
auto compose (const typename LastType<Rest...>::type::domain& x, const F& f, const Rest&... rest) {
return f(compose(x, rest...));
}
int main() {
Function<std::string, int> f([](const std::string& s) {return s.length();});
Function<int, double> g([](int x) {return x + 0.5;});
Function<double, int> h([](double d) {return int(d+1);});
std::cout << compose("hello", g, f) << '\n'; // g(f("hello")) = 5.5
std::cout << compose("hello", h, g, f) << '\n'; // h(g(f("hello"))) = 6
}
Having done that, I thought it would be a trivial task to adapt the above code so that I get the exact syntax I want (i.e. with "hello" being at the end of the list), but it is turning more difficult than I thought. I attempted the following, which does not compile:
#include <iostream>
#include <functional>
#include <tuple>
#include <string>
template <typename D, typename R>
struct Function {
using domain = const D&;
using range = R;
using function = std::function<range(domain)>;
const function& f;
Function (const function& f) : f(f) {}
range operator()(domain x) const {return f(x);}
};
template <typename F, typename G>
typename G::range compose (const G& g, const F& f, const typename F::domain& x) {
return g(f(x));
}
template <typename F, typename... Rest>
auto compose (const F& f, const Rest&... rest) {
return f(compose(rest...));
}
int main() {
Function<std::string, int> f([](const std::string& s) {return s.length();});
Function<int, double> g([](int x) {return x + 0.5;});
Function<double, int> h([](double d) {return int(d+1);});
std::cout << compose(g, f, "hello") << '\n'; // g(f("hello")) = 5.5
std::cout << compose(h, g, f, "hello") << '\n'; // h(g(f("hello"))) = 6
}
And I don't know how to fix it. Can anybody help me fix this?
A new idea I've come up with is to define compose_, which will reorder the arguments of args... (by some std::tuple manipulation) so that the first element goes last and then passing that argument pack to compose. This looks very messy though, and even if it works, there must be a more direct (and shorter) solution.
It looks like the following also works:
template <typename T>
const T& compose (const T& t) {
return t;
}
template <typename F, typename... Rest>
typename F::range compose(const F& f, Rest... rest) {
return f(compose(rest...));
}
what about in this way?
#include <iostream>
#include <functional>
#include <tuple>
#include <string>
template <typename D, typename R>
struct Function {
using domain = const D&;
using range = R;
using function = std::function<range(domain)>;
const function& f;
Function (const function& f) : f(f) {}
range operator()(domain x) const {return f(x);}
};
template <typename F, typename X = typename F::domain>
typename F::range compose (const F& f, const X & x) {
return f(x);
}
template <typename F, typename... Rest>
typename F::range compose (const F& f, const Rest&... rest) {
return f(compose(rest...));
}
int main() {
Function<std::string, int> f([](const std::string& s) {return s.length();});
Function<int, double> g([](int x) {return x + 0.5;});
Function<double, int> h([](double d) {return int(d+1);});
std::cout << compose(g, f, "hello") << '\n'; // g(f("hello")) = 5.5
std::cout << compose(h, g, f, "hello") << '\n'; // h(g(f("hello"))) = 6
}
You can use auto for the returning type of compose() only in c++14 (if I'm not wrong).
Your version doesn't compile because your variadic version of compose() uses N variadic types and N arguments when the final (not varidic) uses 2 types and 3 arguments. In other words, the variadic version lost the final argument.
Your version doesn't compile because the final (not variadic version) is never used: the compiler chooses the variadic version. Adding typename X = typename F::domain (and changing const typename F::domain& with const X&) the final version is preferred and your code should compile (with c++14, at least) [corrected by Piotr Skotnicki; thanks]
p.s.: sorry for my bad English.

Binary search using variadic templates and lambda functions

Consider this,
struct Person {
std::string name;
Person (const std::string& n) : name(n) {}
std::string getName(int, char) const {return name;} // int, char play no role in this
// simple example, but let's suppose that they are needed.
} *Bob = new Person("Bob"), *Frank = new Person("Frank"), *Mark = new Person("Mark"),
*Tom = new Person("Tom"), *Zack = new Person("Zack");
const std::vector<Person*> people = {Bob, Frank, Mark, Tom, Zack};
Because people is sorted by name, we can carry out a binary search to find the element of people with a specific name. I want the call for this to look something like
Person* person = binarySearch (people, "Tom",
[](Person* p, int n, char c) {return p->getName(n,c);},
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a');
so the template function binarySearch can be used generically. I got it working with the following:
#include <iostream>
#include <string>
#include <vector>
#include <functional>
struct Person {
std::string name;
Person (const std::string& n) : name(n) {}
std::string getName(int, char) const {return name;} // int, char play no role in this
// simple example, but let's supposes that they are needed.
} *Bob = new Person("Bob"), *Frank = new Person("Frank"), *Mark = new Person("Mark"),
*Tom = new Person("Tom"), *Zack = new Person("Zack");
const std::vector<Person*> people = {Bob, Frank, Mark, Tom, Zack};
template <typename Container, typename Ret>
typename Container::value_type binarySearch (const Container& container, const Ret& value,
std::function<Ret(const typename Container::value_type&, int, char)> f,
std::function<bool(const Ret&, const Ret&)> comp,
typename Container::difference_type low, typename Container::difference_type high,
int n, char c) {
if (low > high)
std::cout << "Error! Not found!\n";
const typename Container::difference_type mid = (low + high) / 2;
const Ret& r = f(container[mid], n, c);
if (r == value)
return container[mid];
if (comp(r, value))
return binarySearch (container, value, f, comp, mid + 1, high, n, c);
return binarySearch (container, value, f, comp, low, mid - 1, n, c);
}
template <typename Container, typename Ret>
typename Container::value_type binarySearch (const Container& container, const Ret& value,
std::function<Ret(const typename Container::value_type&, int, char)> f,
std::function<bool(const Ret&, const Ret&)> comp, int n, char c) {
return binarySearch (container, value, f, comp, 0, container.size() - 1, n, c);
}
int main() {
const Person* person = binarySearch<std::vector<Person*>, std::string>
(people, "Tom", &Person::getName,
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a');
std::cout << person->getName(5,'a') << '\n'; // Tom
}
But now for reasons I don't understand, I'm not able to replace the specific arguments int, char with Args.... You can go ahead and place Args... args and args... where needed in the above code, and it won't compile. What is wrong here? How to carry out this last step in the generalization? Or should the whole method be changed?
This is what I tried:
template <typename Container, typename Ret, typename... Args>
typename Container::value_type binarySearch (const Container& container, const Ret& value,
std::function<Ret(const typename Container::value_type&, Args...)> f,
std::function<bool(const Ret&, const Ret&)> comp,
typename Container::difference_type low, typename Container::difference_type high,
Args... args) {
if (low > high)
std::cout << "Error! Not found!\n";
const typename Container::difference_type mid = (low + high) / 2;
const Ret& r = f(container[mid], args...);
if (r == value)
return container[mid];
if (comp(r, value))
return binarySearch (container, value, f, comp, mid + 1, high, args...);
return binarySearch (container, value, f, comp, low, mid - 1, args...);
}
template <typename Container, typename Ret, typename... Args>
typename Container::value_type binarySearch (const Container& container, const Ret& value,
std::function<Ret(const typename Container::value_type&, Args...)> f,
std::function<bool(const Ret&, const Ret&)> comp, Args... args) {
return binarySearch (container, value, f, comp, 0, container.size() - 1, args...);
}
int main() {
const Person* person = binarySearch<std::vector<Person*>, std::string> (people, "Tom",
&Person::getName,
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a');
std::cout << person->getName(5,'a') << '\n';
}
GCC 4.9.2:
[Error] no matching function for call to 'binarySearch(std::vector<Person*>&, const char [4], main()::__lambda0, main()::__lambda1, int, char)'
template argument deduction/substitution failed:
[Note] 'main()::__lambda0' is not derived from 'std::function<std::basic_string<char>(Person* const&, Args ...)>'
Update:
Having studied Yakk's solution, I've adapted my solution to the following (using more first principles instead of std::equal_range):
#include <iostream>
#include <iterator>
template <typename Container, typename T, typename Comparator = std::less<T>>
typename Container::value_type binarySearchRandomAccessIterator (const Container& container, T&& value, Comparator&& compare, typename Container::difference_type low, typename Container::difference_type high) {
if (low > high)
{std::cout << "Error! Not found!\n"; return container[high];}
const typename Container::difference_type mid = (low + high) / 2;
const auto& t = compare.function(container[mid]); // Using 'const T& t' does not compile.
if (t == value)
return container[mid];
if (compare.comparator(t, value)) // 't' is less than 'value' according to compare.comparator, so search in the top half.
return binarySearchRandomAccessIterator (container, value, compare, mid + 1, high);
return binarySearchRandomAccessIterator (container, value, compare, low, mid - 1); // i.e. 'value' is less than 't' according to compare.comparator, so search in the bottom half.
}
template <typename ForwardIterator, typename T, typename Comparator = std::less<T>>
typename std::iterator_traits<ForwardIterator>::value_type binarySearchNonRandomAccessIterator (ForwardIterator first, ForwardIterator last, T&& value, Comparator&& compare) {
ForwardIterator it;
typename std::iterator_traits<ForwardIterator>::difference_type count, step;
count = std::distance(first, last);
while (count > 0) { // Binary search using iterators carried out.
it = first;
step = count / 2;
std::advance(it, step); // This is done in O(step) time since ForwardIterator is not a random-access iterator (else it is done in constant time). But the good news is that 'step' becomes half as small with each iteration of this loop.
const auto& t = compare.function(*it); // Using 'const T& t' does not compile.
if (compare.comparator(t, value)) { // 't' is less than 'value' according to compare.comparator, so search in the top half.
first = ++it; // Thus first will move to one past the half-way point, and we search from there.
count -= step + 1; // count is decreased by half plus 1.
}
else // 't' is greater than 'value' according to compare.comparator, so remain in the bottom half.
count = step; // 'count' and 'step' are both decreased by half.
}
if (compare.function(*first) != value)
std::cout << "Error! Not found!\n";
return *first;
}
template <typename Container, typename T, typename Comparator = std::less<T>> // Actually the version below could be used if Container has a random-access iterator. It would be with the same time complexity since std::advance has O(1) time complexity for random-access iterators.
typename std::enable_if<std::is_same<typename std::iterator_traits<typename Container::iterator>::iterator_category, std::random_access_iterator_tag>::value, typename Container::value_type>::type
binarySearch (const Container& container, T&& value, Comparator&& compare = {}) {
std::cout << "Calling binarySearchWithRandomAccessIterator...\n";
return binarySearchRandomAccessIterator (container, value, compare, 0, container.size() - 1);
}
// Overload used if Container does not have a random-access iterator.
template <typename Container, typename T, typename Comparator = std::less<T>>
typename std::enable_if<!std::is_same<typename std::iterator_traits<typename Container::iterator>::iterator_category, std::random_access_iterator_tag>::value, typename Container::value_type>::type
binarySearch (const Container& container, T&& value, Comparator&& compare = {}) {
std::cout << "Calling binarySearchNonRandomAccessIterator...\n";
return binarySearchNonRandomAccessIterator (std::begin(container), std::end(container), value, compare);
}
template <typename Function, typename Comparator>
struct FunctionAndComparator {
Function function;
Comparator comparator;
FunctionAndComparator (Function&& f, Comparator&& c) : function(std::forward<Function>(f)), comparator(std::forward<Comparator>(c)) {}
};
template <typename Function, typename Comparator = std::less<>>
FunctionAndComparator<std::decay_t<Function>, std::decay_t<Comparator>> functionAndComparator (Function&& f, Comparator&& c = {}) {
return {std::forward<Function>(f), std::forward<Comparator>(c)};
}
#include <string>
#include <vector>
#include <list>
struct Person {
std::string name;
Person (const std::string& n) : name(n) {}
std::string getName (int, char) const {return name;} // int, char play no role in this simple example, but let's supposes that they are needed.
} *Bob = new Person("Bob"), *Frank = new Person("Frank"), *Mark = new Person("Mark"), *Tom = new Person("Tom"), *Zack = new Person("Zack");
const std::vector<Person*> peopleVector = {Bob, Frank, Mark, Tom, Zack};
const std::list<Person*> peopleList = {Bob, Frank, Mark, Tom, Zack};
int main() {
Person* tom = binarySearch (peopleVector, "Tom", functionAndComparator([](const Person* p) {return p->getName(5,'a');}, [](const std::string& x, const std::string& y) {return x.compare(y) < 0;}));
if (tom) std::cout << tom->getName(5,'a') << " found.\n";
Person* bob = binarySearch (peopleVector, "Bob", functionAndComparator([](const Person* p) {return p->getName(3,'k');})); // The default comparator, std::less<std::string>, is actually the same as the comparator used above.
if (bob) std::cout << bob->getName(3,'k') << " found.\n";
Person* frank = binarySearch (peopleList, "Frank", functionAndComparator([](const Person* p) {return p->getName(8,'b');}));
if (frank) std::cout << frank->getName(8,'b') << " found.\n";
Person* zack = binarySearch (peopleList, "Zack", functionAndComparator([](const Person* p) {return p->getName(2,'c');}));
if (zack) std::cout << zack->getName(2,'c') << " found.\n";
Person* mark = binarySearch (peopleList, "Mark", functionAndComparator([](const Person* p) {return p->getName(6,'d');}));
if (mark) std::cout << mark->getName(6,'d') << " found.\n";
}
In my opinion
Person* person = binarySearch (people, "Tom",
[](Person* p, int n, char c) {return p->getName(n,c);},
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a');
is a horrible syntax. Your binarySearch function is resposible for way too many things.
But first, what went wrong: Your ambiguous error occurred because a lambda is not a std::function. It tries to deduce the std::function type from the lambda, and fails because they are unrelated types. The ability to deduce Args... from somewhere else doesn't help.
You can wrap your std::function arguments in:
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<class T>using identity=type_t<tag<T>>;
identity< std::function< whatever... > > and your code will start to compile (as Args... is deduced elsewhere). identity<?> blocks template type deduction on that argument, so the compiler doesn't try anymore, and instead deduces the type from the other arguments.
However, this is not a good solution.
A better solution is to make the type of f and c be F and C -- don't make them into std::functions at all. This removes pointless type-erasure overhead, and removes the need for identity<?>
This is still not a good solution, because your template function does a bunch of things, few of them well. Instead, decompose your operation into simpler problems, then compose those together:
First, we already have std::equal_range, which is going to be a better binary search than any you are likely to write. Writing a function that returns a single element, and takes a container, seems reasonable, as working with iterators is annoying.
To pull this off, first we write some range-based boilerplate:
namespace adl_aux {
using std::begin; using std::end;
template<class R>
auto adl_begin(R&&)->decltype(begin(std::declval<R>()));
template<class R>
auto adl_end(R&&)->decltype(end(std::declval<R>()));
}
template<class R>
using adl_begin = decltype(adl_aux::adl_begin(std::declval<R>));
template<class R>
using adl_end = decltype(adl_aux::adl_end(std::declval<R>));
template<class R>using iterator_t = adl_begin<R>;
template<class R>using value_t = std::remove_reference_t<decltype(*std::declval<iterator_t<R>>())>;
This allows us to support std:: containers and arrays and 3rd party iterable containers and ranges. The adl_ stuff does argument dependent lookup of begin and end for us. The iterator_t and value_t does SFINAE-friendly determining of the value and iterator type of a range.
Now, bin_search on top of that boilerplate:
template<class R, class T, class F=std::less<T>>
value_t<R>* bin_search( R&& r, T&& t, F&& f={} ) {
using std::begin; using std::end;
auto range = std::equal_range( begin(r), end(r), std::forward<T>(t), std::forward<F>(f) );
if (range.first==range.second) return nullptr;
return std::addressof( *range.first ); // in case someone overloaded `&`
}
which returns a pointer to the element t under the ordering f presuming R is sorted under it if it exists, and otherwise nullptr.
The next part is your ordering mess:
[](Person* p, int n, char c) {return p->getName(n,c);},
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}, 5, 'a'
first, get rid of that args...:
[](int n, char c){
return [n,c](Person* p) {return p->getName(n,c);}
}(5,'a'),
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}
if you really need to do it on one line, do the binding directly.
Next, we want order_by:
template<class F, class C>
struct order_by_t : private F, private C {
F const& f() const { return *this; }
C const& c() const { return *this; }
template<class T>
auto f(T&&t)const
->decltype( std::declval<F const&>()(std::declval<T>()) )
{
return f()(std::forward<T>(t));
}
template<class T, class... Unused> // Unused to force lower priority
auto f(T&&t, Unused&&... ) const
-> std::decay_t<T>
{ return std::forward<T>(t); }
template<class Lhs, class Rhs>
bool operator()(Lhs&& lhs, Rhs&& rhs) const {
return c()( f(std::forward<Lhs>(lhs)), f(std::forward<Rhs>(rhs)) );
}
template<class F0, class C0>
order_by_t( F0&& f_, C0&& c_ ):
F(std::forward<F0>(f_)), C(std::forward<C0>(c_))
{}
};
template<class C=std::less<>, class F>
auto order_by( F&& f, C&& c={} )
-> order_by_t<std::decay_t<F>, std::decay_t<C>>
{ return {std::forward<F>(f), std::forward<C>(c)}; }
order_by takes a projection from a domain to a range, and optionally an ordering on that range, and produces an ordering on the domain.
order_by(
[](int n, char c){
return [n,c](Person const* p)
->decltype(p->getName(n,c)) // SFINAE enabled
{return p->getName(n,c);};
}(5,'a'),
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}
}
is now an ordering on Person const*s that follows your requirements.
We then feed this into bin_search:
auto ordering = order_by(
[](int n, char c){
return [n,c](Person const* p)
->decltype(p->getName(n,c)) // SFINAE enabled
{return p->getName(n,c);}
}(5,'a'),
[](const std::string& x, const std::string& y) {return x.compare(y) < 0;}
);
Person*const* p = bin_search( people, "Tom", ordering );
now, some care had to be made to make order_by a "transparent" function object, where it accepts both things that can be projected (under the projection) and cannot be (which are passed directly to the comparator).
This requires that the projection operation be SFINAE friendly (ie, that it "fail early"). To this end, I explicitly determined its return type. (Below we see that this is not required, but it may be in more complex situations).
Live example.
Amusingly, your [](const std::string& x, const std::string& y) {return x.compare(y) < 0;} agrees with operator< on a std::string, so you could drop that (and make order_by simpler). However, I suspect your real use case needs it, and it is a useful feature to fortify order_by with.
Finally, note that this part:
[](int n, char c){
return [n,c](Person const* p)
->decltype(p->getName(n,c)) // SFINAE enabled
{return p->getName(n,c);}
}(5,'a'),
is ugly, and can be replaced with:
[](Person const* p)
->decltype(p->getName(5,'a')) // SFINAE enabled
{return p->getName(5,'a');}
which is less ugly. Also, because the parameter check of the lambda is enough, we can drop the SFINAE explicit return type stuff:
[](Person const* p)
{return p->getName(5,'a');}
and we are done. Simpler example:
auto ordering = order_by(
[](Person const* p)
{return p->getName(5,'a');}
);
Person*const* p = bin_search( people, "Tom", ordering );
or even:
Person*const* p = bin_search( people, "Tom",
order_by( [](Person const* p) {return p->getName(5,'a');} )
);
which looks far less ugly, no?
Oh, and:
using std::literals;
Person*const* p = bin_search( people, "Tom"s,
order_by( [](Person const* p) {return p->getName(5,'a');} )
);
might have better performance, as it will avoid repeatedly constructing a std::string("Tom") on every comparison. Similarly, a getName that returns a std::string const& (if possible) can also give a performance boost. The "projection lambda" might have to have a ->decltype(auto) in it to realise this second boost.
I used some C++14 above. The std::remove_reference_t<?> (and similar) aliases can be replaced with typename std::remove_reference<?>::type, or you can write your own _t aliases. The advice to use decltype(auto) can be replaced with decltype(the return expression) in C++11.
order_by_t uses inheritance to store F and C because they are likely to be empty classes, so I wanted to exploit the empty base optimization.

How do I bind a ::std::vector of arguments to a functor?

I'm trying to make this program compile properly:
#include <vector>
#include <iostream>
int f(int a, int b)
{
::std::cout << "f(" << a << ", " << b << ") == " << (a + b) << '\n';
return a + b;
}
template <typename R, typename V>
R bind_vec(R (*f)(), const V &vec, int idx=0)
{
return f();
}
template <typename R, typename V, typename Arg1, typename... ArgT>
R bind_vec(R (*f)(Arg1, ArgT...), const V &vec, int idx=0)
{
const Arg1 &arg = vec[idx];
auto call = [arg, f](ArgT... args) -> R {
return (*f)(arg, args...);
};
return bind_vec(call, vec, idx+1);
}
int foo()
{
::std::vector<int> x = {1, 2};
return bind_vec(f, x);
}
Ideally I'd like bind_vec to take an arbitrary functor as an argument instead of just a function pointer. The idea is to pull the function arguments from a ::std::vector at compile time.
This isn't the final use for this, but it's a stepping stone to where I want to go. What I'm really doing is generating wrapper functions that unwrap their arguments from promises in a future/promise type system at compile time. These wrapper functions will themselves be promises.
In my ultimate use-case I can count on the functors being ::std::functions. But it would be nice to have an idea of how it should work for more general functors as well since I think this is a broadly interesting problem.
OK, first off, detecting the arity of a functor can be done, but it's a bit involved and best left to a separate question. Let's assume you will specify the arity of the functor in the call. Similarly, there are ways to obtain the return type of a callable object, but that's also beyond the scope of this question. Let's just assume the return type is void for now.
So we want to say,
call(F f, C v);
and that should say f(v[0], v[1], ..., v[n-1]), where f has arity n.
Here's an approach:
template <unsigned int N, typename Functor, typename Container>
void call(Functor const & f, Container const & c)
{
call_helper<N == 0, Functor, Container, N>::engage(f, c);
}
We need the helper:
#include <functional>
#include <cassert>
template <bool Done, typename Functor, typename Container,
unsigned int N, unsigned int ...I>
struct call_helper
{
static void engage(Functor const & f, Container const & c)
{
call_helper<sizeof...(I) + 1 == N, Functor, Container,
N, I..., sizeof...(I)>::engage(f, c);
}
};
template <typename Functor, typename Container,
unsigned int N, unsigned int ...I>
struct call_helper<true, Functor, Container, N, I...>
{
static void engage(Functor const & f, Container const & c)
{
assert(c.size() >= N);
f(c[I]...);
}
};
Example:
#include <vector>
#include <iostream>
void f(int a, int b) { std::cout << "You said: " << a << ", " << b << "\n"; }
struct Func
{
void operator()(int a, int b) const
{ std::cout << "Functor: " << a << "::" << b << "\n"; }
};
int main()
{
std::vector<int> v { 20, 30 };
call<2>(f, v);
call<2>(Func(), v);
}
Notes: In a more advanced version, I would deduce the arity of the callable object with some more template machinery, and I would also deduce the return type. For this to work, you'll need several specializations for free functions and various CV-qualified class member functions, though, and so this would be getting too large for this question.
Something like this is easily possible for (member) function pointers, but for functors with potentially overloaded operator(), this gets a dang lot harder. If we assume that you have a way to tell how many arguments a function takes (and assume that the container actually has that many elements), you can just use the indices trick to expand the vector into an argument list, for example with std::next and a begin() iterator:
#include <utility>
#include <iterator>
template<class F, class Args, unsigned... Is>
auto invoke(F&& f, Args& cont, seq<Is...>)
-> decltype(std::forward<F>(f)(*std::next(cont.begin(), Is)...))
{
return std::forward<F>(f)(*std::next(cont.begin(), Is)...);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
This implementation works really nice for random-access containers, but not so well for forward and especially input ones. To make those work in a performant fashion, you might try to go the route of incrementing the iterator with every expanded step, but you'll run into a problem: Evaluation order of arguments to a function is unspecified, so you'll very likely pass the arguments in the wrong order.
Luckily, there is a way to force evaluation left-to-right: The list-initialization syntax. Now we just need a context where that can be used to pass arguments, and a possible one would be to construct an object, pass the function and the arguments through the constructor, and call the function in there. However, you lose the ability to retrieve the returned value, since constructors can't return a value.
Something I thought of is to create an array of iterators, which point to the correct element, and expanding those again in a second step where they are dereferenced.
#include <utility>
template<class T> using Alias = T; // for temporary arrays
template<class F, class It, unsigned N, unsigned... Is>
auto invoke_2(F&& f, It (&&args)[N], seq<Is...>)
-> decltype(std::forward<F>(f)(*args[Is]...))
{
return std::forward<F>(f)(*args[Is]...);
}
template<class F, class Args, unsigned... Is>
auto invoke_1(F&& f, Args& cont, seq<Is...> s)
-> decltype(invoke_2(std::forward<F>(f), std::declval<decltype(cont.begin())[sizeof...(Is)]>(), s))
{
auto it = cont.begin();
return invoke_2(std::forward<F>(f), Alias<decltype(it)[]>{(void(Is), ++it)...}, s);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
The code was tested against GCC 4.7.2 and works as advertised.
Since you said that the functors you are getting passed are std::functions, getting the number of arguments they take is really easy:
template<class F> struct function_arity;
// if you have the 'Signature' of a 'std::function' handy
template<class R, class... Args>
struct function_arity<R(Args...)>
: std::integral_constant<std::size_t, sizeof...(Args)>{};
// if you only have the 'std::function' available
template<class R, class... Args>
struct function_arity<std::function<R(Args...)>>
: function_arity<R(Args...)>{};
Note that you don't even need function_arity to make invoke from above work for std::function:
template<class R, class... Ts, class Args>
R invoke(std::function<R(Ts...)> const& f, Args& cont){
return invoke_1(f, cont, gen_seq<sizeof...(Ts)>{})
}
I managed to do what you want. It's simplest to explain if I leave it as not deducing the correct return type at first, I'll show how to add that later on:
#include <vector>
#include <type_traits>
namespace {
int f(int a, int b) { return 0; }
}
template <typename ...Args>
constexpr unsigned nb_args(int (*)(Args...)) {
return sizeof...(Args);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V&, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) == nb_args(F()),void>::type
{
f(std::forward<Args>(args)...);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V& v, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) < nb_args(F()),void>::type
{
bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args)));
}
int main() {
bind_vec(&f, std::vector<int>(), 1);
return 0;
}
There are two versions of this bind_vec - one is enabled if the parameter pack is the right size for the function. The other is enabled if it is still too small. The first version simply dispatches the call using the parameter pack, whilst the second version gets the next element (as determined by the size of the parameter pack) and recurses.
There SFINAE is done on the return type of the function in order that it not interfer with the deduction of the types, but this means it needs to be done after the function since it needs to know about F. There's a helper function that finds the number of arguments needed to call a function pointer.
To deduce the return types also we can use decltype with the function pointer:
#include <vector>
#include <type_traits>
namespace {
int f(int a, int b) { return 0; }
}
template <typename ...Args>
constexpr unsigned nb_args(int (*)(Args...)) {
return sizeof...(Args);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V&, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) == nb_args(F()),decltype(f(std::forward<Args>(args)...))>::type
{
return f(std::forward<Args>(args)...);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V& v, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) < nb_args(F()),decltype(bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args))))>::type
{
return bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args)));
}
int main() {
bind_vec(&f, std::vector<int>(), 1);
return 0;
}