lundi 8 juillet 2019

Is there a nice way to implement a conditional type with default fail case?

For implementing a conditional type I highly enjoy std::conditional_t as it keeps the code short and very readable:

template<std::size_t N>
using bit_type =
    std::conditional_t<N == std::size_t{  8 }, std::uint8_t,
    std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
    std::conditional_t<N == std::size_t{ 32 }, std::uint32_t, 
    std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>;

using it works quite intuitively:

bit_type<8u> a;  // == std::uint8_t
bit_type<16u> b; // == std::uint16_t
bit_type<32u> c; // == std::uint32_t
bit_type<64u> d; // == std::uint64_t

But since this is a pure conditional type there must be a default type - void, in this case. Therefore if N is any other value said type yields:

bit_type<500u> f; // == void

Now this doesn't compile, but the yielding type is still valid.

Meaning you could say bit_type<500>* f; and would have a valid program!

So is there a nice way let compilation fail when the the fail case of an conditional type is reached?


One idea immediately would be to replace the last std::conditional_t with std::enable_if_t:

template<std::size_t N>
using bit_type =
    std::conditional_t<N == std::size_t{  8 }, std::uint8_t,
    std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
    std::conditional_t<N == std::size_t{ 32 }, std::uint32_t, 
    std::enable_if_t<  N == std::size_t{ 64 }, std::uint64_t>>>>;

The problem with that is that templates are always fully evaluated, meaning that the std::enable_if_t is always fully evaluated - and that will fail if N != std::size_t{ 64 }. Urgh.


My current go-to workaround to this is rather clumsy introducing a struct and 3 using declarations:

template<std::size_t N>
struct bit_type {
private:
    using vtype =
        std::conditional_t<N == std::size_t{ 8 }, std::uint8_t,
        std::conditional_t<N == std::size_t{ 16 }, std::uint16_t,
        std::conditional_t<N == std::size_t{ 32 }, std::uint32_t,
        std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>;

public:
    using type = std::enable_if_t<!std::is_same_v<vtype, void>, vtype>;
};

template<std::size_t N>
using bit_type_t = bit_type<N>::type;

static_assert(std::is_same_v<bit_type_t<64>, std::uint64_t>, "");

Which generally works, but I dislike it as it adds so much stuff, I might as well just use template specialization. Is there a readable, short solution?

Aucun commentaire:

Enregistrer un commentaire