Home > Back-end >  Expanding a typelist into a variadic template
Expanding a typelist into a variadic template

Time:05-11

I'm working with several different variant implementations and I need to be able to convert between them. Specifically, std::variant, _variant_t, and a few others. I'm trying to create a list of types to use as both a way to instantiate std::variants and as a means to iterate through a list of types in a function to check if the std::variant holds a value of that particular type. My initial thought was something like this.

template <typename ...Args>
struct type_list{
}

I would like to use this as such, then use variadic recursion in a function to test and return the appropriate _variant_t. I won't have a problem with the function implementations if I could just find a way to extract the types from the type list.

using unsigned_types = typelist<uint8_t, uint16_t, uint32_t>;
using unsigned_variant = std::variant<unsigned_types::types>;

template <typename T, typename ...Args>
_variant_t to_variant_t(unsigned_variant& var) {
    // auto val = std::get_if<T>(&var);
}

template<typename ...Args>
_variant_t to_variant_t(unsigned_variant& var) {
    // Iterate over Args types to check if variant contains type
    
}

// Example syntax
int main() {
    auto std_var = unsigned_variant((uint8_t)42);
    auto _var = to_variant_t<unsigned_types::types>();
}

Here is the brute force way I could do this, but I feel it could be more elegant and less repetitive using templates, hence the above constructs.

_variant_t to_variant_t(const unsigned_variant& variant) {
    auto uint8_ptr = std::get_if<uint8_t>(&variant);
    if (uint8_ptr != nullptr)
        return _variant_t((unsigned char)*uint8_ptr);

    auto uint16_ptr = std::get_if<uint16_t>(&variant);
    if (uint16_ptr != nullptr)
        return _variant_t((unsigned short)*uint16_ptr);

    // And so on for each unsigned integer type
}

How do I go about storing and extracting the types from type_list? I've tried the following...

template <typename ...Args>
struct type_list{
    using types = Args...; // Parameter pack cannot be expanded in this context.
}

But obviously it doesn't compile. I'm missing something here. Any ideas?

CodePudding user response:

You can write a meta function like

template <typename... Types>
auto variant_from_type_list(type_list<Types...>) -> std::variant<Types...>;

and combined with an helper alias of

template <typename TypeList>
using variant_from_type_list_t = decltype(variant_from_type_list(std::declval<TypeList>()));

lets you do

using unsigned_variant = variant_from_type_list_t<unsigned_types>;

CodePudding user response:

You might have your transformation directly in your type_list:

template <typename ...Args>
struct type_list{
    template <template <typename...> class C>
    using transform_into = C<Args...>;
};

And then

using unsigned_types = type_list<uint8_t, uint16_t, uint32_t>;
using unsigned_variant = unsigned_types::transform_into<std::variant>;

Demo

CodePudding user response:

Parameter packs cannot be stored directly, unfortunately.

Extraction is usually done using partial specialization.

#include <stdint.h>
#include <variant>

template <typename... Args> struct type_list {};

template <template <typename...> class var_impl, typename tlist>
struct make_var {};

template <template <typename...> class var_impl, typename... Args>
struct make_var<var_impl, type_list<Args...>> {
    using type = var_impl<Args...>;
};

template <template <typename...> class var_impl, typename tlist>
using make_var_t = typename make_var<var_impl, tlist>::type;
template <typename tlist>
using make_std_var_t = typename make_var<std::variant, tlist>::type;

using unsigned_types = type_list<uint8_t, uint16_t, uint32_t>;
using unsigned_variant = make_std_var_t<unsigned_types>;

static_assert(std::is_same_v<unsigned_variant,
                             std::variant<uint8_t, uint16_t, uint32_t>>);

CodePudding user response:

Use class template partial specializationto extract types of typelist

#include <variant>
#include <cstdint>

template <typename... Args>
struct typelist{};

template <typename TypeList, template <typename...> typename Template>
struct transform_to;

template <
  template <typename...> typename TypeList,
  typename... Args,
  template <typename...> typename Template>
struct transform_to<TypeList<Args...>, Template> {
  using type = Template<Args...>;
};

using unsigned_types = typelist<uint8_t, uint16_t, uint32_t>;
using unsigned_variant = transform_to<unsigned_types, std::variant>::type;

static_assert(
  std::is_same_v<unsigned_variant, std::variant<uint8_t, uint16_t, uint32_t>>);

Demo

  •  Tags:  
  • c
  • Related