Select an integer type based on template integer parameter

前端 未结 6 1693
予麋鹿
予麋鹿 2020-12-16 16:15

I would like to create a class template which takes an unsigned integer parameter and has a member u_ whose type is the smallest unsigned integer type that will

相关标签:
6条回答
  • 2020-12-16 16:29

    This piece of metaprogramming trickery achieves it:

    template<unsigned int Y> struct typed
    {
        typedef typename typed<(Y & (Y - 1)) == 0 ? Y / 2 : (Y & (Y - 1))>::type type;
    };
    
    template<> struct typed<0>
    {
        typedef std::uint8_t type;
    };
    
    template<> struct typed<256>
    {
        typedef std::uint16_t type;
    };
    
    template<> struct typed<65536>
    {
        typedef std::uint32_t type;
    };
    
    /* ToDo - add more specialisations as necessary*/
    
    template<unsigned k> class A
    {
    public:
        unsigned static const k_ = k; /*constexpr if your compiler supports it*/
        typename typed<k>::type u_;
    };
    

    The usage is exactly in the question.

    The unspecialised template version takes the previous type. typed<0> blocks the static recursion. The other specialisations act as anchoring points for the appropriate types.

    The compile-time evaluable (Y & (Y - 1)) == 0 ? Y / 2 : (Y & (Y - 1)) reduces the number of instantiations by removing the rightmost bit of Y until a power of 2 is reached, and then divides by 2 subsequently to that. (Acknowledge @Jarod42).

    0 讨论(0)
  • 2020-12-16 16:30

    If what we want is: given a uint64_t template parameter, give the smallest unsigned type that is capable of representing it, then what we really want is just a simple iteration at compile time.

    namespace details {
        template <typename T>
        struct tag {
            using type = T;
        };
    
        // base case: just fail
        template <uint64_t V, typename... >
        struct min_unsigned_type;
    
        // recursive case: check using numeric_limits
        template <uint64_t V, typename T, typename... Ts>
        struct min_unsigned_type<V, T, Ts...>
        : std::conditional_t<(V <= std::numeric_limits<T>::max()),
                             tag<T>,
                             min_unsigned_type<V, Ts...>>
        { };
    }
    

    Then just an alias to wrap things together:

    template <uint64_t V>
    using min_unsigned_type = 
        typename details::min_unsigned_type<V, 
            uint8_t, uint16_t, uint32_t, uint64_t>::type;
    

    This has the added advantage of being able to easily specify how far you want to go, or even being able to add larger unsigned types if that's something you find necessary.

    And finally your class:

    template <uint64_t V>
    struct A {
        static constexpr uint64_t k_ = V;
        min_unsigned_type<V> u_;
    };
    
    0 讨论(0)
  • 2020-12-16 16:37
    template<uintmax_t Id>
    struct A;
    
    template<>
    struct A<0>{
        enum {id = 0};
        using type = uint8_t;
    };
    
    template<>
    struct A<255>{
        enum {id = 255};
        using type = uint8_t;
    };
    
    template<>
    struct A<256>{
        enum {id = 256};
        using type = uint16_t;
    };
    
    int main(){
        typename A<255>::type a0 = 255; // uint8_t
        typename A<256>::type a1 = 256; // uint16_t
    }
    

    But I think you want to have a class so that every value under 256 will define uint8_t, every value under 65536 will define uint16_t etc. etc.

    So just incase, this is how you would do that:

    template<uintmax_t Value>
    struct A{
        enum {value = Value};
        using type = std::conditional_t<
            (Value <= std::numeric_limits<uint8_t>::max()), uint8_t,
                std::conditional_t<
                    (Value <= std::numeric_limits<uint16_t>::max()), uint16_t,
                        std::conditional_t<
                            (Value <= std::numeric_limits<uint32_t>::max()), uint32_t
                                std::conditional_t<
                                    (Value <= std::numeric_limits<uint64_t>::max()), uint64_t, uintmax_t
                                >
                        >
                >
        >;
    };
    

    And here's a live example

    0 讨论(0)
  • 2020-12-16 16:38

    With C++11 you can use std::conditional:

    #include <cassert>
    #include <cstdint>
    #include <limits>
    #include <type_traits>
    
    template<std::uint32_t K>
    class A
    {
    public:
      decltype(K) k_ = K;
    
      typename std::conditional<K <= UINT8_MAX,
                                std::uint8_t,
                                typename std::conditional<K <= UINT16_MAX,
                                std::uint16_t,
                                std::uint32_t>::type>::type u_;
    };
    
    int main()
    {
      A<100> small;
      A<1000> medium;
      A<100000> large;
    
      assert( (std::is_same<std::uint8_t, decltype(small.u_)>::value) );
      assert( (std::is_same<std::uint16_t, decltype(medium.u_)>::value) );
      assert( (std::is_same<std::uint32_t, decltype(large.u_)>::value) );
    }
    

    This assumes that:

    • uint8_t in template<uint8_t k, typename > is just an oversight or, as pointed out in Aaron McDaid's comment, the example doesn't work. I changed uint8_t to uint32_t (the example can be smoothly extended to uint64_t);
    • int k_ = k; is a in-class member initialization. For a constant definition you can use enum {k_ = K};
    • u_ in typename u_; is a data member. If it's a type definition you can easily change the example using a typedef or a type-alias (C++11).

    If you cannot use C++11 there's boost::conditional or you can write your own version:

    template<bool, class T, class F>
    struct conditional { typedef T type; };
    
    template<class T, class F>
    struct conditional<false, T, F> { typedef F type; }; 
    
    0 讨论(0)
  • 2020-12-16 16:39

    You can use using declarations for partially bound templates:

    template<uint8_t k>
    using A_uint8 = A<k, uint8_t>;
    
    template<uint8_t k>
    using A_uint16 = A<k, uint16_t>;
    

    then:

    A_uint8<7> hello; // -> A<7, uint8_t>;
    A_uint16<256> hello; // -> A<256, uint16_t>;
    
    0 讨论(0)
  • 2020-12-16 16:39

    Thanks very much. You guys rock! By the way, I'm using:

    typedef typename std::conditional<k <= UINT8_MAX,
                            std::uint8_t,
                            typename std::conditional<k <= UINT16_MAX,
                            std::uint16_t,
                            typename std::conditional<k <= UINT32_MAX,
                            std::uint32_t,
                            std::uint64_t>::type>::type>::type TypeU;
    
    std::pair<TypeU, std::shared_ptr<ProtoData>> min_;
    std::pair<TypeU, std::shared_ptr<ProtoData>> max_;
    
    0 讨论(0)
提交回复
热议问题