Filling multi-dimensional objects (arrays, nested standard containers, etc) in C++ has always been annoying to me. Nested loops are usually used. For example, to fill a 3-dimensional object obj with value v, you may write something like
for (auto& x : obj) {
for (auto& y : x) {
for (auto& z : y) {
z = v;
}
}
}
Such loops are code noise, being tedious to write and also hamper code reading. I'm thinking about writing a function template to perform the filling. Ideally, with such a function template, you should be able to write something like
fill(obj, v);
Any ideas?
Currently, I have a function template make_multi() to make multi-dimensional objects. So you can do
// assuming `obj` is a 3x3x3 nested `std::vector`
obj = make_multi<std::vector>(v, 3, 3, 3);
Besides being more code than the ideal case, this solution is a performance nightmare. Ultimately, I have to look for better ways.
You can write a flattened foreach:
namespace detail {
template <typename Range, typename Func>
constexpr auto flat_foreach(Range&& r, Func& f, int)
-> decltype(void(f(std::forward<Range>(r)))) {f(std::forward<Range>(r));}
template <typename Range, typename Func>
constexpr void flat_foreach(Range&& r, Func& f...) {
for (auto&& i : r)
flat_foreach(std::forward<decltype(i)>(i), f, 0);
}
}
template <typename Range, typename Func>
constexpr void flat_foreach(Range&& r, Func f)
{detail::flat_foreach(std::forward<Range>(r), f, 0);}
Demo. This is the non-greedy approach, passing the range off to the given function as shallow as possible.
Here is the greedy counterpart to #Columbo's excellent answer:
namespace detail {
using std::begin;
using std::end;
template<typename Elem, typename Func>
constexpr void flat_foreach(Elem&& e, Func& f, long) {
f(std::forward<Elem>(e));
}
template<typename Range, typename Func>
constexpr auto flat_foreach(Range&& r, Func& f, int)
-> decltype(begin(r), void(end(r))) {
for (auto&& i : r) {
flat_foreach(std::forward<decltype(i)>(i), f, 0);
}
}
}
template<typename Range, typename Func>
constexpr void flat_foreach(Range&& r, Func f) {
detail::flat_foreach(std::forward<Range>(r), f, 0);
}
Online Demo
The general advantage to this approach is that one can pass in a generic lambda/functor for f.
Related
I need to convert elements of a std::vector to types based on a template parameter and call a function with these parameters. In pseudocode:
template <typename T...>
void foo(std::vector<std::string> v) {
if (v.size() != sizeof...(T))
throw std::runtime_error("Bad");
bar(convert<T0>(v[0]), convert<T1>(v[1]), ..., convert<Tn>(v[n]));
}
My problem is how to obtain the element indices from the parameter pack, I think there will be some kind of a trick using fold expressions, but I can't figure it out.
If you know that the number of elements in a vector is equal to the parameter pack size, you can solve this problem by adding one level of indirection:
template<typename... T, std::size_t... is>
void foo_impl(const std::vector<std::string>& v, std::index_sequence<is...>) {
bar(convert<T>(v[is])...);
}
template<typename... T>
void foo(const std::vector<std::string>& v) {
assert(v.size() == sizeof...(T));
foo_impl<T...>(v, std::index_sequence_for<T...>{});
}
The idea here is to expand two packs, Ts... and is..., which have equal sizes, simultaneously.
C++20 solution:
template<typename... T>
void foo(const std::vector<std::string>& v) {
assert(v.size() == sizeof...(T));
[&v]<std::size_t... is>(std::index_sequence<is...>) {
bar(convert<T>(v[is])...);
}(std::index_sequence_for<T...>{});
}
You could solve this by using an std::integer_sequence to access the elements of the vector.
namespace detail
{
template <typename...T, size_t...I>
void foo(std::vector<std::string>& v, std::index_sequence<I...>) {
bar(convert<T>(v[I])...);
}
}
template <typename...T>
void foo(std::vector<std::string>& v) {
if (v.size() != sizeof...(T))
throw std::runtime_error("Bad");
detail::foo<T...>(v, std::index_sequence_for<T...>{});
}
On Godbolt: Link
I am wondering if it is possible to write a C++ equivalent of the Python function map, using the automatic return type deduction feature. What I have in mind is something like this:
vector<int> input({1,2,3});
auto output=apply(input,[](int num){return num*num;});
//output should be a vector {1,4,9}
I do know about std::transform, but in the present state of affairs, writing a range loop seems easier.
Baum mit Augen's answer is most of the way there. Just takes a few more steps to support anything that is for-each-able:
template <typename C, typename F>
auto apply(C&& container, F&& func)
{
using std::begin;
using std::end;
using E = std::decay_t<decltype(std::forward<F>(func)(
*begin(std::forward<C>(container))))>;
std::vector<E> result;
auto first = begin(std::forward<C>(container));
auto last = end(std::forward<C>(container));
result.reserve(std::distance(first, last));
for (; first != last; ++first) {
result.push_back(std::forward<F>(func)(*first));
}
return result;
}
We can even go one step further and make this SFINAE-able by not using C++14 auto deduction and instead moving the failure up to the deduction phase. Start with a helper for begin/end:
namespace adl_helper {
using std::begin;
using std::end;
template <typename C>
auto adl_begin(C&& c) -> decltype(begin(std::forward<C>(c))) {
return begin(std::forward<C>(c));
}
template <typename C>
auto adl_end(C&& c) -> decltype(end(std::forward<C>(c))) {
return end(std::forward<C>(c));
}
}
using adl_helper::adl_begin;
using adl_helper::adl_end;
And then use that to deduce E earlier:
using adl_helper::adl_begin;
using adl_helper::adl_end;
template <typename C,
typename F,
typename E = std::decay_t<decltype(std::declval<F>()(
*adl_begin(std::declval<C>())
))>
>
std::vector<E> apply(C&& container, F&& func)
{
/* mostly same as before, except using adl_begin/end instead
of unqualified begin/end with using
*/
}
Now we can test at compile time if some container/function pair is apply-able, and the error is a deduction failure instead of a usage failre:
int arr[] = {1, 2, 3};
auto x = apply(arr, []{ return 'A'; });
main.cpp: In function 'int main()':
main.cpp:45:52: error: no matching function for call to 'apply(int [3], main()::<lambda()>)'
auto x = apply(arr, []() -> char { return 'A'; });
^
main.cpp:29:16: note: candidate: template<class C, class F, class E> std::vector<E> apply(C&&, F&&)
std::vector<E> apply(C&& container, F&& func)
^
main.cpp:29:16: note: template argument deduction/substitution failed:
main.cpp:25:50: error: no match for call to '(main()::<lambda()>) (int&)'
typename E = decltype(std::declval<F>()(
^
As pointed out, this would not handle a container of input iterators well. So let's fix it. We need something to determine the size of the container. If the container has a size() member function, we can use that. Otherwise if the iterators do not have category input_iterator_tag (don't know of any other way to distinguish input iterators...), we can use that. Otherwise, we're kind of out of luck. A good way of doing decreasing order of preference like this is to introduce a chooser hierarchy:
namespace details {
template <int I> struct chooser : chooser<I-1> { };
template <> struct chooser<0> { };
}
And then just walk down:
namespace details {
template <typename C>
auto size(C& container, chooser<2>) -> decltype(container.size(), void())
{
return container.size();
}
template <typename C,
typename It = decltype(adl_begin(std::declval<C&>()))
>
auto size(C& container, chooser<1>)
-> std::enable_if_t<
!std::is_same<std::input_iterator_tag,
typename std::iterator_traits<It>::iterator_category
>::value,
size_t>
{
return std::distance(adl_begin(container), adl_end(container));
}
template <typename C>
size_t size(C& container, chooser<0>)
{
return 1; // well, we have no idea
}
}
template <typename C>
size_t size(C& container)
{
return size(container, details::chooser<10>{});
}
Then we can use size() to reserve() our vector to the best of our ability:
template <typename C,
typename F,
typename E = std::decay_t<decltype(std::declval<F>()(
*adl_begin(std::declval<C>())
))>
>
std::vector<E> apply(C&& container, F&& func)
{
std::vector<E> result;
result.reserve(size(container));
for (auto&& elem : container) {
result.push_back(std::forward<F>(func)(std::forward<decltype(elem)>(elem)));
}
return result;
}
This can certainly be done and would probably look something like this:
template <class Container, class Function>
auto apply (const Container &cont, Function fun) {
std::vector< typename
std::result_of<Function(const typename Container::value_type&)>::type> ret;
ret.reserve(cont.size());
for (const auto &v : cont) {
ret.push_back(fun(v));
}
return ret;
}
If you want to be super general and handle C arrays and everything, you might need to add a couple of overloads for the special cases.
Live example
This has already been discussed in comments, but I think it should also be given as an answer:
The function std::transform from <algorithm> does what you want.
The following code works for me:
#include <vector>
#include <algorithm>
using namespace std;
//...
vector<int> input({1,2,3});
transform(input.begin(), input.end(), input.begin(), [](int num){return num*num;});
This works with your example, and with most of the containers. I use std::transform, because it can be optimized for each stl iterator. I started out from Baum mit Augen's answer, which was deleted later.
template<typename Container, typename Function>
using _mapT = std::vector<typename std::result_of<Function(const typename Container::value_type&)>::type>;
template <typename Container, typename Function>
_mapT<Container, Function> map(const Container &container, Function &&f)
{
_mapT<Container, Function> ret; ret.reserve(container.size());
std::transform(container.begin(), container.end(), std::back_inserter(ret), std::forward<Function>(f));
return ret;
}
I was watching the second part of Walter Brown's CppCon2014 talk on template metaprogramming, during which he discussed the uses of his novel void_t<> construction. During his presentation Peter Sommerlad asked him a question that I didn't quite understand. (link goes directly to the question, the code under discussion took place directly before that)
Sommerlad asked
Walter, would that mean we actually can implement concepts lite right now?
to which Walter responded
Oh yeah! I've done it ... It doesn't have quite the same syntax.
I understood this exchange to be about Concepts Lite. Is this pattern really that versatile? For whatever reason, I am not seeing it. Can someone explain (or sketch) how something like this might look? Is this just about enable_if and defining traits, or what was the questioner referring to?
The void_t template is defined as follows:
template<class ...> using void_t = void;
He uses this then to detect if type statements are well formed, using this to implement the is_copy_assignable type trait:
//helper type
template<class T>
using copy_assignment_t
= decltype(declval<T&>() = declval<T const&>());
//base case template
template<class T, class=void>
struct is_copy_assignable : std::false_type {};
//SFINAE version only for types where copy_assignment_t<T> is well-formed.
template<class T>
struct is_copy_assignable<T, void_t<copy_assignment_t<T>>>
: std::is_same<copy_assignment_t<T>,T&> {};
Because of the talk, I understand how this example works, but I don't see how we get from here to something like Concepts Lite.
Yes, concepts lite basically dresses up SFINAE. Plus it allows deeper introspection to allow for better overloading. However that only works if the concept predicates are defined as concept bool. The improved overloading does not work with the current concept predicates, but conditional overloading can be used. Lets look how we can define predicates, constrain templates, and overload functions in C++14. This is kind of long, but it goes over how to create all of the tools needed to accomplish this in C++14.
Defining Predicates
First, it is kind of ugly to read the predicate with all the std::declval and decltype everywhere. Instead, we can take advantage of the fact that we can constrain a function using a trailing decltype(from Eric Niebler’s blog post here), like this:
struct Incrementable
{
template<class T>
auto requires_(T&& x) -> decltype(++x);
};
So if ++x is not valid, then the requires_ member function is not callable. So we can create a models trait that just checks if requires_ is callable using void_t:
template<class Concept, class Enable=void>
struct models
: std::false_type
{};
template<class Concept, class... Ts>
struct models<Concept(Ts...), void_t<
decltype(std::declval<Concept>().requires_(std::declval<Ts>()...))
>>
: std::true_type
{};
Constraining Templates
So when we want to constrain the template based on the concept, we will still need to use enable_if, but we can use this macro to help make it cleaner:
#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
So we can define an increment function that is constrained based on Incrementable concept:
template<class T, REQUIRES(models<Incrementable(T)>())>
void increment(T& x)
{
++x;
}
So if we call increment with something that is not Incrementable, we will get an error like this:
test.cpp:23:5: error: no matching function for call to 'incrementable'
incrementable(f);
^~~~~~~~~~~~~
test.cpp:11:19: note: candidate template ignored: disabled by 'enable_if' [with T = foo]
template<class T, REQUIRES(models<Incrementable(T)>())>
^
Overloading Functions
Now if we want to do overloading, we want to use conditional overloading. Say we want to create an std::advance using concept predicates, we could define it like this(for now we will ignore the decrementable case):
struct Incrementable
{
template<class T>
auto requires_(T&& x) -> decltype(++x);
};
struct Advanceable
{
template<class T, class I>
auto requires_(T&& x, I&& i) -> decltype(x += i);
};
template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())>
void advance(Iterator& it, int n)
{
it += n;
}
template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())>
void advance(Iterator& it, int n)
{
while (n--) ++it;
}
However, this causes an ambiguous overload(In concepts lite this would still be an ambiguous overload unless we change our predicates to refer to the other predicates in a concept bool) when its used with std::vector iterator. What we want to do is order the calls, which we can do using conditional overloading. It can be thought of writing something like this(which is not valid C++):
template<class Iterator>
void advance(Iterator& it, int n) if (models<Advanceable(Iterator, int)>())
{
it += n;
}
else if (models<Incrementable(Iterator)>())
{
while (n--) ++it;
}
So if the first function isn't called, it will call the next function. So lets start by implementing it for two functions. We will create a class called basic_conditional which accepts two function objects as template parameters:
struct Callable
{
template<class F, class... Ts>
auto requires_(F&& f, Ts&&... xs) -> decltype(
f(std::forward<Ts>(xs)...)
);
};
template<class F1, class F2>
struct basic_conditional
{
// We don't need to use a requires clause here because the trailing
// `decltype` will constrain the template for us.
template<class... Ts>
auto operator()(Ts&&... xs) -> decltype(F1()(std::forward<Ts>(xs)...))
{
return F1()(std::forward<Ts>(xs)...);
}
// Here we add a requires clause to make this function callable only if
// `F1` is not callable.
template<class... Ts, REQUIRES(!models<Callable(F1, Ts&&...)>())>
auto operator()(Ts&&... xs) -> decltype(F2()(std::forward<Ts>(xs)...))
{
return F2()(std::forward<Ts>(xs)...);
}
};
So now that means we need to define our functions as functions objects instead:
struct advance_advanceable
{
template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())>
void operator()(Iterator& it, int n) const
{
it += n;
}
};
struct advance_incrementable
{
template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())>
void operator()(Iterator& it, int n) const
{
while (n--) ++it;
}
};
static conditional<advance_advanceable, advance_incrementable> advance = {};
So now if we try to use it with an std::vector:
std::vector<int> v = { 1, 2, 3, 4, 5, 6 };
auto iterator = v.begin();
advance(iterator, 4);
std::cout << *iterator << std::endl;
It will compile and print out 5.
However, std::advance actually has three overloads, so we can use the basic_conditional to implement conditional that works for any number of functions using recursion:
template<class F, class... Fs>
struct conditional : basic_conditional<F, conditional<Fs...>>
{};
template<class F>
struct conditional<F> : F
{};
So, now we can write the full std::advance like this:
struct Incrementable
{
template<class T>
auto requires_(T&& x) -> decltype(++x);
};
struct Decrementable
{
template<class T>
auto requires_(T&& x) -> decltype(--x);
};
struct Advanceable
{
template<class T, class I>
auto requires_(T&& x, I&& i) -> decltype(x += i);
};
struct advance_advanceable
{
template<class Iterator, REQUIRES(models<Advanceable(Iterator, int)>())>
void operator()(Iterator& it, int n) const
{
it += n;
}
};
struct advance_decrementable
{
template<class Iterator, REQUIRES(models<Decrementable(Iterator)>())>
void operator()(Iterator& it, int n) const
{
if (n > 0) while (n--) ++it;
else
{
n *= -1;
while (n--) --it;
}
}
};
struct advance_incrementable
{
template<class Iterator, REQUIRES(models<Incrementable(Iterator)>())>
void operator()(Iterator& it, int n) const
{
while (n--) ++it;
}
};
static conditional<advance_advanceable, advance_decrementable, advance_incrementable> advance = {};
Overloading With Lambdas
However, additionally, we could use lambdas to write it instead of function objects which can help make it cleaner to write. So we use this STATIC_LAMBDA macro to construct lambdas at compile time:
struct wrapper_factor
{
template<class F>
constexpr wrapper<F> operator += (F*)
{
return {};
}
};
struct addr_add
{
template<class T>
friend typename std::remove_reference<T>::type *operator+(addr_add, T &&t)
{
return &t;
}
};
#define STATIC_LAMBDA wrapper_factor() += true ? nullptr : addr_add() + []
And add a make_conditional function that is constexpr:
template<class... Fs>
constexpr conditional<Fs...> make_conditional(Fs...)
{
return {};
}
Then we can now write the advance function like this:
constexpr const advance = make_conditional(
STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Advanceable(decltype(it), int)>()))
{
it += n;
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Decrementable(decltype(it))>()))
{
if (n > 0) while (n--) ++it;
else
{
n *= -1;
while (n--) --it;
}
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(models<Incrementable(decltype(it))>()))
{
while (n--) ++it;
}
);
Which is little more compact and readable than using the function object versions.
Additionally, we could define a modeled function to reduce down the decltype ugliness:
template<class Concept, class... Ts>
constexpr auto modeled(Ts&&...)
{
return models<Concept(Ts...)>();
}
constexpr const advance = make_conditional(
STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Advanceable>(it, n)))
{
it += n;
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Decrementable>(it)))
{
if (n > 0) while (n--) ++it;
else
{
n *= -1;
while (n--) --it;
}
},
STATIC_LAMBDA(auto& it, int n, REQUIRES(modeled<Incrementable>(it)))
{
while (n--) ++it;
}
);
Finally, if you are interested in using existing library solutions(rather than rolling your own like I've shown). There is the Tick library that provides a framework for defining concepts and constraining templates. And the Fit library can handle the functions and overloading.
Is it possible to iterate over all elements in a struct or class?
For example if I have a struct of three elements of different type:
struct A {
classA a;
classB b;
classC c;
};
then I need some iterator such that a method next() would give me the value
of the next element. The problem is that as you see, the values have different types.
Nope, not with the language as it is.
You could do it by deriving your classes from a common base, and then implementing your own iterator to return pointers to each item as the iterator is traversed.
Alternatively put the items in a std::vector and use that to provide the iteration.
No, there is no reflection in C++, (yet, there are murmurs about static reflection coming one day).
Anyway, there is a way to work around this, to an extent - first of all, you'll need a (temporary) tuple with references to your data members.
Then you will need a construct "iterating" over the tuple, such as:
void applyToAll() { }
template <typename Lambda, typename... Lambdas>
void applyToAll(Lambda&& closure, Lambdas&&... closures) {
std::forward<Lambda>(closure)();
applyToAll(std::forward<Lambdas>(closures)...);
}
// use your favourite sequence-making trick
template <unsigned... Is>
struct _Sequence {
typedef _Sequence<Is...> type;
};
template <unsigned Max, unsigned... Is>
struct _MakeSequence : _MakeSequence<Max - 1, Max - 1, Is...> { };
template <unsigned... Is>
struct _MakeSequence<0, Is...> : _Sequence<Is...> { };
template <typename Tuple, typename Functor, unsigned... Is>
void _foreachElemInTuple(_Sequence<Is...>, Tuple&& t, Functor&& f) {
applyToAll(
[&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
);
}
template <typename Tuple, typename Functor>
void foreachElemInTuple(Tuple&& t, Functor&& f) {
_foreachElemInTuple(
_MakeSequence<std::tuple_size<
typename std::decay<Tuple>::type>::value>(),
std::forward<Tuple>(t), std::forward<Functor>(f)
);
}
Then you can call foreachElemInTuple(yourTuple, some_adapter()).
Your adapter will look like:
struct some_adapter {
template <typename... Args>
// A little bit of C++14, you can also just -> decltype the thing
decltype(auto) operator()(Args&& ... args) const {
return doStuff(std::forward<Args>(args)...);
}
};
As everyone else says, you cannot directly iterate over data members of a
class. However, it is not difficult to do it indirectly, provided of course that
you can access each of the data members you want to iterate over. The idea
in essense, as per ScarletAmaranth's solution, is to iterate over an std::tuple
of references to those data members.
The following program shows how to obtain such a tuple, using std::forward_as_tuple,
and another way to do the iterating by compiletime recursion, without
auxiliary apparatus.
#include <tuple>
/* You want to be able do something with the values of the members of an `A`
in turn.
*/
struct A
{
char ch;
int i;
double d;
// May also have members of class type. It doesn't matter
};
/* 1) Provide yourself with the means of creating a sequence that contains
references to the data members of a given `A`
*/
std::tuple<char const &, int const &, double const &> get_A_vals(A const & a)
{
return std::forward_as_tuple(a.ch,a.i,a.d);
}
/* 2) Provide yourself with a means of applying some operation, `Func`,
to each element of an `std::tuple`
*/
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const &, Func) {}
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const & tpl, Func func)
{
func(std::get<I>(tpl));
for_each_in_tuple<I + 1>(tpl,func);
}
/* 3) Combine 1) and 2) to apply `Func` over the members of an `A`
*/
template<typename Func>
void for_each_in_A(A const & a, Func func)
{
for_each_in_tuple(get_A_vals(a),func);
}
// Testing...
#include <iostream>
// A specimen operation: just prints its argument
struct printer
{
template<typename T>
void operator () (T && t)
{
std::cout << t << std::endl;
}
};
int main()
{
A a{'a',1,2.0};
for_each_in_A(a,printer());
return 0;
}
// EOF
The program outputs:
a
1
2
If you have control of the structs or classes over whose members you need to
iterate, you may consider whether it is practical simply to dispense with them
and use the corresponding std::tuples everywhere.
Code built with gcc 4.8.2 and clang 3.3, -std=c++11.
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;
}