Creating a variant type of all possible map of key-value types at compile time, where the key and value types are specified from a tuple of types
Asked Answered
T

2

1

Currently, I have a variant of map types, where I hard-code all variations of key-value pairs, as such:

// for example, if we support std::string and int types as key-value pair
using MapCombinator = std::variant<
    std::map<std::string, std::string>, 
    std::map<std::string, int>, 
    std::map<int, std::string>, 
    std::map<int, int>>;

In the real case, I need to support key-value pairs of all fundamental types, in addition to std::string. This is why, I would like to only specify a tuple of types, something more like this:

using KVTypes = std::tuple<std::string, int, etc...>;
using MapCombinator = MapCombinatorImpl::type;

where MapCombinatorImpl contains the template-meta-programming logic that creates the final variant type. I would expect something as such:

template<typename... SupportedTypes>
struct MapCombinatorImpl {
    typedef ??? type;
};

I don't want to use macros for this, and if it is too complicated to do with template-meta-programming, I'll support only a manageable subset of fundamental types.

Help appreciated for the implementation with template-meta-programming.

Timberlake answered 26/3, 2018 at 12:46 Comment(1)
std::make_index_sequence<tuple_size<Tuple> * tuple_size<Tuple>>, then std::variant<std::map<std::tuple_element_t<Is / tuple_size<Tuple>, Tuple>, std::tuple_element_t<Is % tuple_size<Tuple>, Tuple>>...>.Clannish
C
2

You might use:

template <typename Tuple, typename Seq> struct MapCombinatorImpl;

template <typename Tuple, std::size_t ... Is>
struct MapCombinatorImpl<Tuple, std::index_sequence<Is...>>
{
    constexpr static std::size_t size = std::tuple_size<Tuple>::value;
    using type = std::variant<std::map<std::tuple_element_t<Is / size, Tuple>,
                                       std::tuple_element_t<Is % size, Tuple>>...>;
};

template <typename ... Ts>
using MapCombinator =
    typename MapCombinatorImpl<std::tuple<Ts...>,
                               std::make_index_sequence<sizeof...(Ts) * sizeof...(Ts)>
                              >::type;

Demo

Clannish answered 26/3, 2018 at 17:48 Comment(0)
A
2

You can use 2-level parameter pack expansion:

namespace detail {
    template<class T, class... Ts>
    using pair_list = std::tuple<std::map<T, Ts>...>;

    template<class Tuple> struct huge_variant_impl;
    template<class... Ts>
    struct huge_variant_impl<std::tuple<Ts...>> {
        using type = std::variant<Ts...>;
    };
}

template<typename... Ts>
struct huge_variant {
    using type = typename detail::huge_variant_impl<
        decltype(std::tuple_cat(std::declval<detail::pair_list<Ts, Ts...>>()...))
        >::type;
};
Albina answered 26/3, 2018 at 13:27 Comment(2)
I used the static assertion from Jarod42's Demo, to verify the type. Assertion failed. So, I printed the demangled name, and verified that the type is incorrect. Got the following: variant<map<int, int>, map<string, string>, map<int, int>, map<string, string>>Timberlake
@Timberlake GCC bug. See clang's result. GCC is confused by 2-level expansion, but it's allowed by the standard. [temp.variadic]/6 Especially the last example.Albina
C
2

You might use:

template <typename Tuple, typename Seq> struct MapCombinatorImpl;

template <typename Tuple, std::size_t ... Is>
struct MapCombinatorImpl<Tuple, std::index_sequence<Is...>>
{
    constexpr static std::size_t size = std::tuple_size<Tuple>::value;
    using type = std::variant<std::map<std::tuple_element_t<Is / size, Tuple>,
                                       std::tuple_element_t<Is % size, Tuple>>...>;
};

template <typename ... Ts>
using MapCombinator =
    typename MapCombinatorImpl<std::tuple<Ts...>,
                               std::make_index_sequence<sizeof...(Ts) * sizeof...(Ts)>
                              >::type;

Demo

Clannish answered 26/3, 2018 at 17:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.