Mam pomysł mapowania wartości wyliczenia do odpowiednich typów danych w czasie kompilacji za pomocą szablonów. Jak mogę to zrobić?

Na przykład


enum DataType {
  UNSINGED_INT; // uint32_t
  INT; // int32_t
  UNSIGNED_CHAR; // uint8_t
  CHAR; // int8_t
}

auto type = MapToDataType<DataType::UNSIGNED_CHAR>; // type will be uint8_t in this case

0
xxx_coder_noscope 20 czerwiec 2021, 23:41

4 odpowiedzi

Najlepsza odpowiedź

Najpierw specjalizujesz się w szablonie zajęć, a następnie używasz szablonu aliasów:

#include <cstdint>

enum class DataType {
    UNSIGNED_CHAR,
    UNSIGNED_INT,
    CHAR,
    INT
};

template <DataType> struct MapToDataType;
template <> struct MapToDataType_t<DataType::UNSIGNED_CHAR> { using type = std::uint8_t; };
template <> struct MapToDataType_t<DataType::UNSIGNED_INT>  { using type = std::unit32_t; };
template <> struct MapToDataType_t<DataType::CHAR>          { using type = std::int8_t; };
template <> struct MapToDataType_t<DataType::INT>           { using type = std::nit32_t; };

template <DataType T>
using MapToDataType = typename MapToDataType_t<T>::type;
2
HolyBlackCat 20 czerwiec 2021, 21:16

Jeśli enum zawiera sekwencję wartości konwertowalną na int, bez dziur i zaczynając od zera (jak w twoim przykładzie), możesz użyć std::tuple jako mapy typów i std::tuple_element, aby wyodrębnić żądany typ.

Napisz więc strukturę pomocniczą w następujący sposób

template <DataType dt>
struct MapToDataType_helper
 {
   using type = typename std::tuple_element<
     static_cast<std::size_t>(dt),
     std::tuple<std::uint32_t,
                std::int32_t,
                std::uint8_t,
                std::int8_t>
                   >::type;
 };

Twoja mapa jest prostym szablonem za pomocą

template <DataType dt>
using MapToDataType = typename MapToDataType_helper<dt>::type;

Na przykład możesz to sprawdzić

static_assert( std::is_same_v<MapToDataType<DataType::UNSIGNED_CHAR>,
                              std::uint8_t> );
3
max66 20 czerwiec 2021, 21:04
template <DataType> struct MapToDataTypeHelper {};
template <> struct MapToDataTypeHelper<INT> {using type = int;};
template <> struct MapToDataTypeHelper<UNSIGNED_INT> {using type = unsigned int;};
// ...

template <DataType X> using MapToDataType = typename MapToDataTypeHelper<X>::type;
MapToDataType<INT> x; // int x;
2
HolyBlackCat 20 czerwiec 2021, 20:43

Niektóre typy i wartości znaczników do przenoszenia wartości i typów w czasie kompilacji:

template<auto x>
using constant_t = std::integral_constant<std::decay_t<decltype(x)>, x>;
template<auto x>
constexpr constant_t<x> constant{};
template<class T>
struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag{};

template<class X>
constexpr void type_map( X ) {}

template<auto X>
using get_type = typename decltype(type_map(constant<X>))::type;

Ok, maszyna jest gotowa.

Teraz po prostu dodaj te:

    inline auto type_map( constant_t<DataType::UNSIGNED_INT> ) { return tag<std::uint32_t>; }
    inline auto type_map( constant_t<DataType::INT> ) { return tag<std::int32_t>; }
    inline auto type_map( constant_t<DataType::UNSIGNED_CHAR> ) { return tag<std::uint8_t>; }
    inline auto type_map( constant_t<DataType::CHAR> ) { return tag<std::int8_t>; }

I to działa.

Obsługa innych wyliczeń to tylko kwestia dodania przeciążeń type_map do przestrzeni nazw danego enum.

Makro

#define TYPE_MAP(FROM,...) \
  inline auto type_map( constant_t<FROM> ) { return tag<__VA_ARGS__>; }

Może sprawić, że będzie mniej gadatliwy.

get_type działa teraz jednolicie na dowolnym wyliczeniu z tą samą maszyną skonfigurowaną na nim.

Przykład na żywo.

1
Yakk - Adam Nevraumont 21 czerwiec 2021, 00:31