Как преобразовать один тип в другой в C ++ 17 с помощью шаблонного метапрограммирования? - PullRequest
0 голосов
/ 01 февраля 2019

Мне нужно реализовать преобразователь типов в C ++, чтобы преобразовать один тип в другой.Например, преобразование int16_t в float и int64_t в double.Я реализовал один, используя специализацию шаблонов:

template<class T>
struct TypeConverter
{

};
template<>
struct TypeConverter<int16_t>
{
       using type = float;
};

template<>
struct TypeConverter<int64_t>
{
       using type = double;
};

TEST(Exp, TypeConveter) {

    static_assert(std::is_same_v<TypeConverter<int16_t>::type, float>);
    static_assert(std::is_same_v<TypeConverter<int64_t>::type, double>);
}

Есть ли лучший (более компактный) способ добиться этого с помощью функций C ++ 17 tmp?

Ответы [ 3 ]

0 голосов
/ 01 февраля 2019

Самым синтетическим способом регистрации типов для преобразователя типов является пара списков типов в std::tuple (или что-то подобное).

Например: если вы хотите преобразоватьstd::int16_t до float, std::int32_t до double и std::int64_t до long double, вы можете using определить пару типов

using list1 = std::tuple<std::int16_t, std::int32_t, std::int64_t>;
using list2 = std::tuple<float, double, long double>;

Теперь, учитывая следующую структуру иобъявленная функция

template <typename, typename, typename>
struct foo
 { using type = std::tuple<>; };

template <typename T1, typename T2>
struct foo<T1, T1, T2>
 { using type = std::tuple<T2>; };

template <typename T, typename ... Ts1, typename ... Ts2>
constexpr auto bar (std::tuple<Ts1...>, std::tuple<Ts2...>)
   -> decltype( std::tuple_cat(
         std::declval<typename foo<T, Ts1, Ts2>::type>()...) );

TypeConverter становится

template <typename T>
using TypeConverter
   = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list1>(),
                                              std::declval<list2>()))>;

Но я предполагаю, что пара списков в двух разных std::tuple s является синтетической, но трудной для понимания и поддержки.

Итак, я предлагаю менее синтетический (но более понятный и поддерживаемый) способ, основанный на одном списке пар типов

using list = std::tuple<std::pair<std::int16_t, float>,
                        std::pair<std::int32_t, double>,
                        std::pair<std::int64_t, long double>>;

Now struct и объявить функцию стать

template <typename, typename>
struct foo
 { using type = std::tuple<>; };

template <typename T1, typename T2>
struct foo<T1, std::pair<T1, T2>>
 { using type = std::tuple<T2>; };

template <typename T, typename ... Ts>
constexpr auto bar (std::tuple<Ts...>)
   -> decltype( std::tuple_cat(
         std::declval<typename foo<T, Ts>::type>()...) );

и TypeConverter

template <typename T>
using TypeConverter
   = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list>()))>;

Ниже приведен пример полной компиляции C ++ 17 с обоими решениями (вы можете включить первое или второе, изменив #if 0)

#include <tuple>
#include <type_traits>

#if 0
template <typename, typename, typename>
struct foo
 { using type = std::tuple<>; };

template <typename T1, typename T2>
struct foo<T1, T1, T2>
 { using type = std::tuple<T2>; };

template <typename T, typename ... Ts1, typename ... Ts2>
constexpr auto bar (std::tuple<Ts1...>, std::tuple<Ts2...>)
   -> decltype( std::tuple_cat(
         std::declval<typename foo<T, Ts1, Ts2>::type>()...) );

using list1 = std::tuple<std::int16_t, std::int32_t, std::int64_t>;
using list2 = std::tuple<float, double, long double>;

template <typename T>
using TypeConverter
   = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list1>(),
                                              std::declval<list2>()))>;
#else

template <typename, typename>
struct foo
 { using type = std::tuple<>; };

template <typename T1, typename T2>
struct foo<T1, std::pair<T1, T2>>
 { using type = std::tuple<T2>; };

template <typename T, typename ... Ts>
constexpr auto bar (std::tuple<Ts...>)
   -> decltype( std::tuple_cat(
         std::declval<typename foo<T, Ts>::type>()...) );

using list = std::tuple<std::pair<std::int16_t, float>,
                        std::pair<std::int32_t, double>,
                        std::pair<std::int64_t, long double>>;

template <typename T>
using TypeConverter
   = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list>()))>;
#endif

int main ()
 {
   static_assert( std::is_same_v<float, TypeConverter<std::int16_t>> );
   static_assert( std::is_same_v<double, TypeConverter<std::int32_t>> );
   static_assert( std::is_same_v<long double, TypeConverter<std::int64_t>> );
 }
0 голосов
/ 01 февраля 2019

Другой простой способ (совместимый с C ++ 11):

template<class T> struct Type {};

float type_convert(Type<int16_t>);
double type_convert(Type<int64_t>);

int main() {
    static_assert(std::is_same_v<decltype(type_convert(Type<int16_t>{})), float>);
    static_assert(std::is_same_v<decltype(type_convert(Type<int64_t>{})), double>);
}

Преимущество этого подхода состоит в том, что он использует поиск по имени в зависимости от аргумента (ADL), чтобы найти соответствующую type_convert функцию декларация (без определение необходимо).Если вам нужно обрабатывать пользовательские типы (UDT), легко добавить соответствующие перегрузки type_convert для них в том же пространстве имен, в котором они объявлены (без необходимости открывать пространство имен признаков, чтобы определить другую специализацию шаблона класса признаков).для вашего UDT).Например:

namespace N { 
    struct MyType;
    long double type_convert(Type<MyType>);
} 

А затем:

// type_convert is found by ADL.    
static_assert(std::is_same_v<decltype(type_convert(Type<N::MyType>{})), long double>);
0 голосов
/ 01 февраля 2019

CE C ++ 17: https://gcc.godbolt.org/z/iY8Qoa

CE C ++ 11: https://gcc.godbolt.org/z/ApuxZj

Код пользователя

#include<TypeMap.h>
#include<cstdlib>
using TMap = TypeMap <
    std::pair<int16_t, float>,
    std::pair<int64_t, double>
>;

void foo() {
    static_assert(std::is_same_v<TMap::get<int16_t>, float>);
    static_assert(std::is_same_v<TMap::get<int64_t>, double>);
    static_assert(std::is_same_v<TMap::get<int>, NotFound>);
}

TypeMap.h

#include<type_traits>
#include<utility>

struct NotFound;

template<class... TPs>
struct TypeMap {
    template<class T>
    using get = NotFound;
};

template<class TP1, class... TPs>
struct TypeMap<TP1, TPs...> {
    template<class T>
    using get = std::conditional_t< std::is_same_v<T, typename TP1::first_type>
            , typename TP1::second_type
            , typename TypeMap<TPs...>::template get<T> >;
};
...