Mapping an integral template parameter value onto a primitive type

微笑、不失礼 提交于 2019-12-01 10:29:58

问题


I want to map a number to a type. For this example I'll make a function that maps a sizeof() result onto a signed primitive type.

I am wondering if there is a better way of doing what I did below in modern C++, which is to take a templated value and convert it into a type. Right now this works in converting a size to a known type, but I can't seem to find anything in the standard library that does what I want. Have I missed something?

If not, is there a better way of doing this or cleaning up this code? For example if somehow in the future we end up having 128 bit types, this wouldn't support that.

#include <iostream>
#include <type_traits>

template <size_t S>
static constexpr auto sizeToType() {
    static_assert(S == 1 or S == 2 or S == 4 or S == 8, "Bad type size");

    if constexpr (S == 1)
        return int8_t{};
    else if constexpr (S == 2)
        return int16_t{};
    else if constexpr (S == 4)
        return int32_t{}; 
    else
        return int64_t{};
}

int main() {
    using MY_TYPE = decltype(sizeToType<2>());

    MY_TYPE myType = MY_TYPE(0xFFFFFFFFFFFFFFFEUL);

    std::cout << sizeof(MY_TYPE) << " bytes" << std::endl;
    std::cout << "MY_TYPE(0xFFFFFFFFFFFFFFFEUL) = " << myType << std::endl;
}

Output (as expected):

2 bytes
MY_TYPE(0xFFFFFFFFFFFFFFFEUL) = -2

回答1:


I would not use C++17 if constexpr for this, instead I'd use template specialization, as it looks more declarative to me.

Something along the following lines:

template<size_t S> struct SizeToType {static_assert(S != S, "Wrong size"); };

template<> struct SizeToType<1> { using type = uint8_t; };
template<> struct SizeToType<2> { using type = uint16_t; };
template<> struct SizeToType<4> { using type = uint32_t; };
template<> struct SizeToType<8> { using type = uint64_t; };

template<size_t S>
using SizeToToTypeT = typename SizeToType<S>::type;

Adding more types would be just adding more specializations (one-liners) here.




回答2:


Not a great solution... but just for fun...

I propose a type traits (with helper and using to select the internal type) that, given a number (the number of bytes) and a list of types select the first type of the list with sizeof() greater or equal (or nothing if there is no such type)

template <std::size_t, typename, typename = std::true_type>
struct sizeTypeH;

template <std::size_t Dim>
struct sizeTypeH<Dim, std::tuple<>, std::true_type>
 { };  // no type found

template <std::size_t Dim, typename T0, typename ... Ts>
struct sizeTypeH<Dim, std::tuple<T0, Ts...>,
                 std::integral_constant<bool, (sizeof(T0) >= Dim)>>
 { using type = T0; };

template <std::size_t Dim, typename T0, typename ... Ts>
struct sizeTypeH<Dim, std::tuple<T0, Ts...>,
                 std::integral_constant<bool, (sizeof(T0) < Dim)>>
   : public sizeTypeH<Dim, std::tuple<Ts...>>
 { };

template <std::size_t Dim, typename ... Ts>
struct sizeType : public sizeTypeH<Dim, std::tuple<Ts...>>
 { };

template <std::size_t Dim, typename ... Ts>
using sizeType_t = typename sizeType<Dim, Ts...>::type;

Now another using that, receiving a integer, call this type traits with the ordered list of intX_t types

template <std::size_t Dim>
using intType_t = sizeType_t<Dim, std::int8_t, std::int16_t, std::int32_t,
                             std::int64_t>;

In my platform I have that the following static_assert() are verified.

   using t8  = intType_t<1u>;
   using t16 = intType_t<2u>;
   using t32 = intType_t<4u>;
   using t64 = intType_t<8u>;

   static_assert( std::is_same<t8, std::int8_t>{}, "!" );
   static_assert( std::is_same<t16, std::int16_t>{}, "!" );
   static_assert( std::is_same<t32, std::int32_t>{}, "!" );
   static_assert( std::is_same<t64, std::int64_t>{}, "!" );

If, tomorrow, is introduced a int128_t, you can simply add it in intType_t using definition.

Attention, please: there is no guaranties that the preceding static_assert() are satisfied.

First of all because the standard guaranties that a byte is at least of 8 bits; but can be more that 8 bits. If a byte is of 16 bits, from

using t16 = intType_t<2u>;

you get a type of 32 bits.

Second because intX_t types are optionals.



来源:https://stackoverflow.com/questions/53488151/mapping-an-integral-template-parameter-value-onto-a-primitive-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!