c++c++17stdtuplestdapply

set a value in std::tuple using a function that takes an index to the std::get


If You want to read data from vector of std::tuple You can use :

#include <algorithm>
#include <iostream>
#include <sstream>
#include <tuple>
#include <variant>
#include <vector>

using example = std::tuple<unsigned long, double, int, float, double>;
using var = std::variant<unsigned long, double, int, float>;
enum example_index
{
    a = 0,
    b = 1,
    c = 2,
    d = 3,
    e = 4,
    MAX = e
};
std::stringstream &operator<<(std::stringstream &stream, const example &e)
{
    std::apply([&stream](auto &&...args) { ((stream << args << ";"), ...); }, e);
    return stream;
}
int main()
{
    std::vector<example> example_v;
    auto tuple_example = std::make_tuple(10, 5.5, -3, 2.2, 4.1);
    example_v.emplace_back(tuple_example);
    example_v.emplace_back(tuple_example);
    std::stringstream stream = std::stringstream();
    for (auto &e : example_v)
    {
        stream << e << std::endl;
    }
    std::cout << stream.str();
    return 0;
}

Can I assign value to std::tuple in function which takes index and std::variant of all possible data types of tuple ?

I tried :

#include <algorithm>
#include <iostream>
#include <sstream>
#include <tuple>
#include <variant>
#include <vector>

using example = std::tuple<unsigned long, double, int, float, double>;
using var = std::variant<unsigned long, double, int, float>;
enum example_index
{
    a = 0,
    b = 1,
    c = 2,
    d = 3,
    e = 4,
    MAX = e
};
bool set_value(example &e, var value, example_index index)
{
    if (index > example_index::MAX)
    {
        return false;
    }
    std::get<index>(e) = value;
    return true;
}
std::stringstream &operator<<(std::stringstream &stream, const example &e)
{
    std::apply([&stream](auto &&...args) { ((stream << args << ";"), ...); }, e);
    return stream;
}
int main()
{
    example e;
    set_value(e, 10, example_index::a);
    set_value(e, 5.5, example_index::b);
    set_value(e, -3, example_index::c);
    set_value(e, 2.2, example_index::d);
    set_value(e, 4.1, example_index::e);
    std::stringstream stream;
    stream << e << std::endl;
    std::cout << stream.str() << std::endl;
    return 0;
}

But I'm getting error : No matching function for call to 'get'.

I know i can assign it static

example e;
std::get<0>(e) = 1;
std::get<1>(e) = 2.0;
std::get<2>(e) = 3;
std::get<3>(e) = 4.5f;
std::get<4>(e) = 5.6;
std::stringstream stream;
stream << e;
std::cout << stream.str() << std::endl;

I also know it can be passed using switch case statement for each field but I feel like it's too much ...


Solution

  • You can use template metaprogramming for your set_value function:

    template<example_index INDEX, typename T>
    bool set_value(example &e, T value)
    {
        static_assert(INDEX<=example_index::MAX,"bad index");
        std::get<INDEX>(e) = value;
        return true;
    }
    

    The call site looks like this:

    set_value<example_index::a>(e, 10 );
    

    That way to compile will generate all versions of set_value that are used in the program.