Liam Goodacre
Liam Goodacre

Reputation: 423

can I initialize an std::tuple from a vector array?

I have an std::vector containing a variant class. I want to construct a tuple with the same data. Is this possible? The normal construction methods for a tuple seem quite restrictive.

                    //In reality, I'm using JUCE::var.
                    // SimpleVariant is here just to make the example code more explicit.
struct SimpleVariant
{
    SimpleVariant(int i) :                a(i), b("") {}
    SimpleVariant(const std::string& s) : a(0), b(s) {}

    operator int() const { return a; }
    operator std::string() const { return b; }

private:
    int a;
    std::string b;
};


template <typename... T>
struct VariantTuple
{
    VariantTuple(const std::vector<SimpleVariant>& v)
    {
        // how do I initialize the tuple here?
    }

private:
    std::tuple<T...> tuple;
};


        std::vector<SimpleVariant> v{ SimpleVariant(1),
                                      SimpleVariant(2),
                                      SimpleVariant("a") };

        VariantTuple<int, int, std::string> t (v);

Some clarifications based on the comments:

I do not need the tuple to match the array term by term, or to deduce the types from the given array. I want to take a given array and then extract Variants that match a certain type. So, for instance, given the above array v, I would like to be able to construct a VariantTuple<int, std::string>, and have it match the terms "1" and "a". This introduces a host of other problems beyond the scope of my original question. But the question I'm interested in right now is whether it is even possible to construct a tuple based on an array in the first place.

Upvotes: 3

Views: 465

Answers (1)

Samik
Samik

Reputation: 575

Well I'm not sure if you're asking to dynamically deduce the number of vector elements and construct the tuple, which isn't possible, but here you go. I've used std::index_sequence to deduce the number of tuple elements depending of VariantTuple's argument size. This requires C++17 as it uses fold-expression.

#include <initializer_list>
#include <string>
#include <vector>
#include <tuple>
#include <utility>
#include <type_traits>
#include <ostream>
#include <iostream>

struct SimpleVariant
{
    SimpleVariant(int i) :                a(i), b("") {}
    SimpleVariant(const std::string& s) : a(0), b(s) {}

    operator int() const {
        return a;
    }

    operator std::string() const {
        return b;
    }

    int a;
    std::string b;
};

template<typename V, size_t... dim, typename... Args>
auto populate_tuple(const V& vec, std::index_sequence<dim...>, const std::tuple<Args...>& t) {
    return std::make_tuple(static_cast<std::remove_reference_t<decltype(std::get<dim>(t))>>(vec.at(dim))...);
}

template<size_t... dim, typename... Args>
std::ostream& dump_tuple(std::ostream& out, const std::tuple<Args...>& tpl, std::index_sequence<dim...>) {
    ((out << std::get<dim>(tpl) << ","), ...);
    return out;
}

template<typename... T>
struct VariantTuple
{
    VariantTuple(const std::vector<SimpleVariant>& v) : tpl(populate_tuple(v, std::make_index_sequence<sizeof...(T)>{}, tpl)) {}

    template<typename... V>
    friend std::ostream& operator <<(std::ostream& out, const VariantTuple<V...>& vt) {
        return dump_tuple(out, vt.tpl, std::make_index_sequence<sizeof...(V)>{});
    }
private:
    std::tuple<T...> tpl;
};

int main() {
    std::vector<SimpleVariant> v { 
        SimpleVariant(1),
        SimpleVariant(2),
        SimpleVariant("a") 
    };
    VariantTuple<int, int, std::string> t (v);
    std::cout << t << std::endl;

    return 0;
}

Upvotes: 1

Related Questions