Call variadic templated function with arguments from a std::vector - c++

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

Related

std::tuple unpack over multiple arguments

I have a function taking pairs of (argument type, data ptr) as a variadic list (C function). I would like to unpack the tuple into that function as follows:
foo(TypeIndex<std::tuple_element_t<I, Tuple>>(), &std::get<I>(tuple));
thus I wrote a following function:
template<typename Tuple, size_t ...I>
void doUnpack(Tuple const& tp, std::index_sequence<I...>)
{
foo((type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp))...);
}
the only problem is comma operator ignores everything on the left uses the right hand side. Imagine type<> function returns 0 for now, so the above evaluates (using input tuple{1,2,3,4,5}) to foo(1,2,3,4,5) instead of foo(0,1, 0,2, 0,3, 0,4, 0,5)
Is there any way to accomplish this?
Code to reproduce:
template<typename Tp>
int type() { return 0; }
template<typename ...Args>
void fun(Args&& ...args)
{
(std::cout << ... << args) << std::endl;
}
template<typename Tuple, size_t ...I>
void doUnpack(Tuple const& tp, std::index_sequence<I...>)
{
fun((type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp))...);
}
int main()
{
doUnpack(std::tuple{1,2,3,4,5}, std::make_index_sequence<5>{});
return 0;
}
The following does what you want:
#include <tuple>
#include <iostream>
template<typename Tp>
int type() {
return 0;
}
template<typename Tuple, size_t... I>
auto doUnpack(Tuple const &tp, std::index_sequence<I...>) {
auto fun = [](auto &&...args) { (std::cout << ... << args) << std::endl; };
std::apply(fun, std::tuple_cat(std::pair{type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp)}...));
}
int main() {
doUnpack(std::tuple{1, 2, 3, 4, 5}, std::make_index_sequence<5>{});
return 0;
}
We use two nice STL-functions. std::tuple_cat takes a bunch of tuples (here pairs of the type and the value of the tuple) and concatenates them to one big tuple. So you go from (type0, val0), (type1, val1)... to (type0, val0, type1, val1, ...). This goes around the problem with the comma operator. After that, we apply the function to that with std::apply. Note that I use a lambda since std::apply needs (basically) a callable with a type and a function template does not qualify for that (whereas a lambda is (basically) a struct with templated operator() which works for that.

How to unpack tuple with unknown size?

Given,
template<typename T>
void foo(T t)
{
std::tie(/*xxx*/)=t;
auto &[/*yyy*/]=t;
}
int main()
{
foo(forward_as_tuple(1,2,3));
foo(forward_as_tuple(1,2,3,4,5));
}
I want foo() to unpack the tuple that's passed to itself.
Can decomposition declarations with auto or std::tie(), handle the unknown tuple sizes like xxx - yyy up there?
If yes, how?
I'm trying to think of another ways, maybe all elements could be pushed back to a vector of that type, once they're got.
std::vector<T> bar;
size_t baz=std::tuple_size<T>::value; //useless because it can't be used as:
for(int i=0; i<baz; i++)
bar.push_back(std::get<i>(t)); //since i isn't constant
Using vectors was just a bad idea that failed.
How else can it be done?
What I'm trying to, shortly is; I want to get tuple elements in a for loop. That's why I think I need them to be extracted somehow.
How to unpack tuple with unknown size?
auto &[/*yyy*/]=t;
You can't.
maybe all elements could be pushed back to a vector of that type
Use std::apply:
std::vector<int> baz;
std::apply([&baz](auto... args) {
(baz.push_back(args), ...);
}, t);
What I'm trying to, shortly is; I want to get tuple elements in a for loop.
You don't need to unpack a tuple to iterate over it. Here's how I'd do it:
template <typename Integer, Integer ...I, typename F>
constexpr void constexpr_for_each(std::integer_sequence<Integer, I...>, F &&func)
{
(func(std::integral_constant<Integer, I>{}) , ...);
}
template <auto N, typename F>
constexpr void constexpr_for(F &&func)
{
if constexpr (N > 0)
{
constexpr_for_each(std::make_integer_sequence<decltype(N), N>{}, std::forward<F>(func));
}
}
template <typename T>
void foo(T t)
{
constexpr_for<std::tuple_size_v<T>>([&](auto index)
{
constexpr auto i = index.value;
std::cout << std::get<i>(t) << '\n';
});
}

Implement function template to fill multi-dimensional objects

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.

Is it possible to apply a generic function over tuple elements?

I have found a for_each loop for tuples which just iterates over the elements and passes them into a function.
namespace std {
template<int I, class Tuple, typename F> struct for_each_impl {
static void for_each(const Tuple& t, F f) {
for_each_impl<I - 1, Tuple, F>::for_each(t, f);
f(get<I>(t));
}
};
template<class Tuple, typename F> struct for_each_impl<0, Tuple, F> {
static void for_each(const Tuple& t, F f) {
f(get<0>(t));
}
};
template<class Tuple, typename F>
void for_each(const Tuple& t, F f) {
for_each_impl<tuple_size<Tuple>::value - 1, Tuple, F>::for_each(t, f);
}
}
.
auto t = std::make_tuple(Foo(),Bar(),Baz());
std::for_each(t,[](???){});
Would it be possible to have a generic function like this?
std::for_each(t,[](T &&t){t.foo();});
In the end I just want to have something that works with every tuple.
std::get<0>(t).foo();
std::get<1>(t).foo();
std::get<2>(t).foo();
...
Maybe this would be easier with macros?
In c++14 you can use a generic lambda expression:
for_each(t, [] (auto&& t) { std::forward<decltype(t)>(t).foo(); });
In c++11 you can declare your own functor:
struct Lambda
{
template <typename T>
void operator()(T&& t) const { std::forward<T>(t).foo(); }
};
for_each(t, Lambda{});
or if, instead, you would like to apply a different function depending on the tuple element's type currently being processed, then once again a custom functor is the way to go:
struct Lambda
{
void operator()(const Foo& foo) const { foo.foo(); }
void operator()(const Bar& bar) const { bar.bar(); }
void operator()(const Baz& baz) const { baz.baz(); }
};
for_each(t, Lambda{});
And as a side note: do not define functions inside the std namespace.

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;
}