std::tuple unpack over multiple arguments - c++

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.

Related

convert nlohmann::basic_json<> to std::tuple c++

I'm trying to call a function with parameters extracted from a JSON file using the nlohmann/json library.
I need to convert the JSON array retrieved from the JSON file to a tuple to call the function with.
The problem is that there's no build-up solution to convert nlohmann::basic_json<> to std::tuple in c++
the source code with comments to explain much more the programming issue :
using namespace std;
#include <tuple>
#include <iostream>
#include <fstream>
#include "json.hpp"
#include <typeinfo>
// for convenience
using json = nlohmann::json;
void f(int a, int b, int c)
{
std::cout << a << ":" << b << ":" << c << std::endl;
}
template <typename Function, typename Tuple, size_t... I>
auto call(Function f, Tuple t, std::index_sequence<I...>)
{
return f(std::get<I>(t)...);
}
template <typename Function, typename Tuple>
auto call(Function f, Tuple t)
{
static constexpr auto size = std::tuple_size<Tuple>::value;
return call(f, t, std::make_index_sequence<size>{});
}
int main()
{
auto tup = std::make_tuple(1, 2, 3);
call(f, tup); //wroks fine
std::ifstream i("tests.json");
json j;
i >> j;
for (auto &element : j)
{
call(f, element["args"]); //issue
//element["args"] is [1,2,3] of type nlohmann::basic_json<>
//res = need to convert element["args"] to tuple
//call function like that : call(f,res)
std::cout << element["args"] << '\n';
}
}
the json file content:
[
{
"args": [1, 2, 3],
"expected": 6
}
]
Let's assume that you only pass non-overloaded functions, function pointers, or function objects with one operator(). Lets also assume that the json object is always an array of parameters.
First we need to make a tuple from the json and the target arguments
template<typename... Args, std::size_t... Is>
std::tuple<Args...> from_json(const json & args, std::index_sequence<Is...>)
{
return { args[Is].get<Args>()... };
}
Then we can defined some overloads for different cases. The function (pointer) case is easy.
template<typename R, typename... Args>
R call(R(*func)(Args...), const json & args)
{
return std::apply(func, from_json<Args...>(args, std::index_sequence_for<Args...>{}));
}
Function objects are harder, we have to look at their operator().
template<typename F, typename R, typename... Args>
R call_impl(F && f, const json & args, R(F::*)(Args...))
{
return std::apply(std::forward<F>(f), from_json<Args...>(args, std::index_sequence_for<Args...>{}));
}
template<typename F>
R call(F && f, const json & args)
{
return call_impl(std::forward<F>(f), args, &F::operator());
}

Variadic function that accepts arguments of same type at compile time and iterate on them

I am looking for a way to implement Variadic function that accepts arguments of same type at compile-time and should be able to iterate on them. The variadic parameters are at the end with all of them having the same type.
Something like below -
void SampleFunc(Other arguments(String may be)..., int... arg)
{
for (const auto& val : arg)
{
// Each argument available here.
}
}
then I will call this function like below -
SampleFunc("String", "{1,2,3,4})
Most important thing is that variadic parameters are hardcoded every time the function is called so I should be able to generate this Variadic argument at compile time.
Right now I am accepting function parameters as shown below -
void SampleFunc(std::string str, std::vector<int>& nums)
But this adds run time cost of constructing a vector every time function is called which I want to avoid.
UPDATE :-
I forgot to mention that this function has other parameters at the start. It is my bad, sorry about that. I have updated my example now.
If the arguments are known at compile-time, in c++17, using fold expressions you can do something like
#include <utility> // std::forward
template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
// Each argument available here.
std::cout << arg << "\n";
}
template <typename... Args>
void SampleFunc(Args&&... args) /* noexcept */
{
(doSomething(std::forward<Args>(args)), ...);
}
Now you could call the function like
SampleFunc(1, 2, 3, 4);
and using doSomething you can do something with each arguments.
(See a Demo Online)
In previous compilers, you could imitate the fold expression via expander trick, as follows
template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
// Each argument available here.
std::cout << arg << "\n";
}
template <typename... Args>
void SampleFunc(Args&&... args) /* noexcept */
{
using dummy = int[];
(void)dummy {
0, (doSomething(std::forward<Args>(args)), 0)...
};
}
(See a Demo Online)
Iterate on variadic arguments is the simplest part: you tagged C++17 so you can use template folding, as suggested by JeJo, or other ways (recursion, initialization of an unused array).
More complicated is impose that all the arguments are exactly of the same type.
Obviously you can use SFINAE to impose that the deduced type are of the same type, but if you pass arguments of the different types, by example
foo(1l, 2l, 3l, 4); // long, long, long, int
when an argument is convertible to the type of the others, the code doesn't compile.
If you accept to pass through an additional function and that your function is a method of a template struct, you can start with an using that select the type from a couple type/index
template <typename T, std::size_t>
using get_type = T;
you can write the template struct as follows
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
Observe that the arguments following str in the operator() are all of type T, where T is the first template argument of the struct.
The additional function is
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
You can call foo() as follows
foo<int>("string", 1, 2, 3, 4l);
Observe that a long value (4l) is accepted because is converted to int.
You can also directly call the bar::operator(), if you prefer
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
but you have to explicit the second template argument so there is some redundancies.
The following is a full compiling example
#include <string>
#include <utility>
#include <iostream>
template <typename T, std::size_t>
using get_type = T;
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
int main ()
{
foo<int>("string", 1, 2, 3, 4l); // a long value is converted to int
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
}
The variadic parameters are at the end with all of them having the same type.
Whereas std::vector might have the overhead of extra allocation, you might simply use std::initializer_list instead (of variadic).
void SampleFunc(std::string str, std::initializer_list<int>& nums)
{
for (int val : nums)
{
// Each argument available here.
}
}
With call similar to
SampleFunc("String", {1, 2, 3, 4});

Is there a way to pass a `constexpr` value into lambda so that it remains `constexpr` inside that lambda?

Here's what I want to do; posting the whole code because it's not too long and also to demonstrate the specific task I'm trying to solve. Basically, I need a way to iterate values from parameter pack by index (the index part is important, even though it's not required in this example).
#include <iostream>
#include <tuple>
#include <type_traits>
template <int First, int Last, typename Functor>
constexpr void static_for(Functor&& f)
{
if constexpr (First < Last)
{
f(std::integral_constant<int, First>{});
static_for<First + 1, Last, Functor>(std::forward<Functor>(f));
}
}
template <size_t index, typename... Args>
auto value_by_index(Args&&... args) noexcept {
return std::get<index>(std::forward_as_tuple(std::forward<Args>(args)...));
}
template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
static_for<0, sizeof...(ValueTypes)>([&](int i) {
auto v = value_by_index<static_cast<size_t>(i), ValueTypes...>(values...);
std::cout << v << std::endl;
});
}
int main()
{
traverse(0.0f, 1, 3.33, "str");
return 0;
}
The compiler error, of course, is:
<source>:24:71: error: 'i' is not a constant expression
If lambdas could have explicit template arguments, i would be such an argument and it would be obvious to the compiler that it's known at compile time. But that's not how lambdas work.
If you want to treat it as an X-Y problem, I suppose I don't specifically need to call a lambda inside my static_for, but I do need to call some piece of code that can access parameter pack(s) of traverse by index, and if traverse was a member function, I need to have access to its this.
Try it online: https://godbolt.org/z/eW4rnm
Use a generic lambda and a constexpr conversion operator c++17:
template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
static_for<0, sizeof...(ValueTypes)>([&](auto I)
// ~~~^
{
auto v = value_by_index<I>(values...);
// ~^~
std::cout << v << std::endl;
});
}
DEMO
Use a template parameter list for the lambda expression c++20:
template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
static_for<0, sizeof...(ValueTypes)>([&]<int I>(std::integral_constant<int, I>)
// ~~~~^ ~^~
{
auto v = value_by_index<I>(values...);
// ~^~
std::cout << v << std::endl;
});
}
DEMO 2
It's too late to play?
Basically, I need a way to iterate values from parameter pack by index (the index part is important, even though it's not required in this example).
Sorry but... what about the good old use of std::make_index_sequence and std::index_sequence ?
Maintaining your value_by_index(), I propose the following C++14 solution based on traverse() with traverse_helper()
template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
{
using unused = int[];
(void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
}
template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
{ traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }
Observe that I've passed also the callable as parameter.
If you can use C++17 (as you tagged), traverse_helper() simply become
template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
{ (f(value_by_index<Is>(vs...)), ...); }
You can call traverse() as follows
traverse([](auto x){ std::cout << x << std::endl; },
0.0f, 1, 3.33, "str");
The following is a full C++14 compiling example
#include <iostream>
#include <tuple>
#include <type_traits>
template <std::size_t I, typename ... As>
auto value_by_index (As && ... as) noexcept
{ return std::get<I>(std::forward_as_tuple(std::forward<As>(as)...)); }
template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
{
using unused = int[];
(void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
}
template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
{ traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }
int main ()
{
traverse([](auto x){ std::cout << x << std::endl; },
0.0f, 1, 3.33, "str");
}

how to assing multiple std::tuple_element as function arguments using variadic size_t templates

I want to create a function that changes multiple values inside a tuple with one call.
template<class... Args>
class EventN {
public:
std::tuple<Args...> mArgs;
EventN(Args... args) : mArgs(args) {}
EventN() {}
template<size_t N> void set(typename std::tuple_element<N-1,Tuple>::type value) {
std::get<N-1>(mArgs) = value; //I use N-1, to start from '1'
}
};
The set function above works as I expect it to:
auto event = new EventN<String,int>();
event->set<1>("testEvent");
event->set<2>(12);
Now, I want to extend the function to:
event->set<1,2>("testEvent", 12);
How can I achieve that? Is there a way to make std::tuple_element<N-1,Tuple>::type variadic?
Try something like this:
template <int ...N, typename ...Args>
void set_all(Args &&... args)
{
int dummy[] = { (set<N>(std::forward<Args>(args)), 0)... };
static_cast<void>(dummy); // unused
}
This works because the different packs are expanded in lockstep. This requires all packs to have the same size, which is enforced at compile time.
The dummy array and the comedic comma operator are just a sleazy way of evaluating all the expressions for their side effects' sake without caring for their value.
Here's a complete little demo:
#include <iostream>
#include <utility>
template <int N, typename T> void set(T && t)
{
std::cout << "Setting " << N << " => " << std::forward<T>(t) << "\n";
}
template <int ...N, typename ...Args>
void set_all(Args &&... args)
{
int dummy[] = { (set<N>(std::forward<Args>(args)), 0)... };
static_cast<void>(dummy);
}
int main()
{
set_all<2, 51, 1>("Hello", 100, true);
}
Observe that at this point there is no constraint on the values of the integers; this constraint only comes in later when you use the integer as a tuple index.

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