Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to convert from std::vector to args

I have a function which takes multiple arguments like this:

void test(const auto&...args)
{
   typedef boost::variant<int, float, std::string> Variant;
   std::vector<Variant> vec = {args...}
}

this works fine - arguments are inside the vector as different types according to the Variant

I would like to fetch arguments from a similar vector and put it back into a function that takes args...

something like this :

args = vec;
test2(args);

void test2(const auto&...args);

how can this be done?

like image 725
serup Avatar asked Feb 01 '17 13:02

serup


3 Answers

No it's not possible. Parameter packs must be determined at compile time, in order to be expanded. Each uniquely-typed parameter pack becomes its own unique function call, created at compile time.

Obviously, the size of a vector is not known until run time. C++ does not work like that, fundamentally.

like image 170
Sam Varshavchik Avatar answered Sep 24 '22 07:09

Sam Varshavchik


It is possible to do this. But since the size of a vector is only known at runtime, errors from mismatched sizes must occur at runtime too.

Here's how I'd do it:

template<std::size_t... S>
void unpack_vector(const std::vector<Variant>& vec, std::index_sequence<S...>) {
    test2(vec[S]...);
}

template<std::size_t size>
void unpack_vector(const std::vector<Variant>& vec) {
    if (vec.size() != size) throw /* choose your error */;
    unpack_vector(vec, std::make_index_sequence<size>());
}

You can then call it like this:

unpack_vector<6>(vec);

Note that this function will send Variant instances to the function test2.

I must admit this might be a bad idea, runtime errors are not there best. I suggest you to review your design in order to not need this.

like image 24
Guillaume Racicot Avatar answered Sep 24 '22 07:09

Guillaume Racicot


based on @Guillaume Racicot answer, I then found this solution (snippet from http://en.cppreference.com/w/cpp/utility/integer_sequence) :

This example shows how a std::tuple can be converted into arguments for a function invocation.

#include <iostream>
#include <tuple>
#include <utility>

template<typename Func, typename Tup, std::size_t... index>
decltype(auto) invoke_helper(Func&& func, Tup&& tup, std::index_sequence<index...>)
{
    return func(std::get<index>(std::forward<Tup>(tup))...);
}

template<typename Func, typename Tup>
decltype(auto) invoke(Func&& func, Tup&& tup)
{
    constexpr auto Size = std::tuple_size<typename std::decay<Tup>::type>::value;
    return invoke_helper(std::forward<Func>(func),
                         std::forward<Tup>(tup),
                         std::make_index_sequence<Size>{});
}

void foo(int a, const std::string& b, float c)
{
    std::cout << a << " , " << b << " , " << c << '\n';
}

int main()
{
    auto args = std::make_tuple(2, "Hello", 3.5);
    invoke(foo, args);
}

Output:

2 , Hello , 3.5

like image 33
serup Avatar answered Sep 24 '22 07:09

serup