Home > Net >  How do I get an int with a maximum/minimum width?
How do I get an int with a maximum/minimum width?

Time:11-22

I'd like to get an int with a certain width for vectorization purposes.

Something like int_atleast< 3 /*bytes*/ > should give int32_t, and int_atmost< 5 > should give the same int32_t.

I tried to implement this with template specialization, but hit a wall because I'd need to specialize every possible argument. I thought of recursion but it seems like an overcomplicated solution to something probably already in the standard. What should I do?

CodePudding user response:

A C 17 solution is surprisingly simple. if constexpr allows us to alter the return type of a function based on a constant expression. This allows one to write the algorithm rather succinctly

namespace detail {
    template<unsigned W>
    auto compute_atleast_integer() {
        if constexpr (W <= 1)
            return uint8_t{};
        else if constexpr (W <= 2)
            return uint16_t{};
        else if constexpr (W <= 4)
            return uint32_t{};
        else if constexpr (W <= 8)
            return uint64_t{};
    }
}

template<unsigned W>
using int_atleast = decltype(detail::compute_atleast_integer<W>());

This also has the emerging property of giving void when no such integer is available. This is a softer error that may be used in a SFINAE context to do something intelligent.

CodePudding user response:

I tried to implement this with template specialization, but hit a wall because I'd need to specialize every possible argument.

Writing a type trait doesn't require a lot of boilerplate code, IMHO, but your milage may vary.

#include <cstdint>
#include <type_traits>

template< unsigned Bits >
    requires (Bits <= 64) // C  20, remove this line if you need C  17
using int_atleast_t
    = std::conditional_t< Bits <= 8, std::int8_t
    , std::conditional_t< Bits <= 16, std::int16_t
    , std::conditional_t< Bits <= 32, std::int32_t, std::int64_t >>>;

constexpr unsigned operator"" _bits (unsigned long long bits) {
    return bits;
}

constexpr unsigned operator"" _bytes (unsigned long long bytes) {
    return bytes * 8;
}

int main()
{
    static_assert(std::is_same_v<int32_t, int_atleast_t< 30_bits >>);
    static_assert(std::is_same_v<int8_t, int_atleast_t< 8_bits >>);
    static_assert(not std::is_same_v<int16_t, int_atleast_t< 3_bytes >>);
}

CodePudding user response:

I'd use a TypeList<Args...> of sorts, then eliminate options based on your filter. I believe this implements your type trait exactly as needed.

For example:

#include <type_traits>
#include <cstddef>

template <typename ... Args>
struct TypeList{};

template <typename List>
struct FrontImpl;
template <typename Front, typename ... Args>
struct FrontImpl<TypeList<Front, Args...>>
{
    using type = Front;
};
template <typename List>
using Front = typename FrontImpl<List>::type;

template <typename T>
struct Id{ using type = T; };

template <typename List>
struct BackImpl;
template <typename ... Args>
struct BackImpl<TypeList<Args...>>
{
    using type = typename decltype((Id<Args>{}, ...))::type;
};
template <typename T>
using Back = typename BackImpl<T>::type;

template <typename F, typename List, typename Result = TypeList<>>
struct FilterImpl;

template <typename F, typename ... Done>
struct FilterImpl<F, TypeList<>, TypeList<Done...>>
{
    using type = TypeList<Done...>;
};

template <typename F, typename Front, typename ... Rest, typename ... Done>
struct FilterImpl<F, TypeList<Front, Rest...>, TypeList<Done...>>
{
    using type = std::conditional_t
    <
        F::template Test<Front>,
        typename FilterImpl<F, TypeList<Rest...>, TypeList<Done..., Front>>::type,
        typename FilterImpl<F, TypeList<Rest...>, TypeList<Done...>>::type
    >;
};

template <typename F, typename List>
using Filter = typename FilterImpl<F, List>::type;

template <typename F, typename List>
using FirstFiltered = Front<Filter<F, List>>;

template <typename F, typename List>
using LastFiltered = Back<Filter<F, List>>;

template <size_t N>
struct SizeAtLeast
{
    template <typename T>
    static constexpr bool Test = sizeof(T) >= N;
};

template <size_t N>
struct SizeAtMost
{
    template <typename T>
    static constexpr bool Test = sizeof(T) <= N;
};

template <size_t N>
struct SizeBetween
{
    template <typename T>
    static constexpr bool Test = N <= sizeof(T) and sizeof(T) <= N;
};

Then using this define your required aliases:

#include <cstdint>

// As per interface requirement
template <size_t N>
using int_atmost = LastFiltered<SizeAtMost<N>, TypeList<int8_t, int16_t, int32_t, int64_t>>;

template <size_t N>
using int_atleast = FirstFiltered<SizeAtLeast<3>, TypeList<int8_t, int16_t, int32_t, int64_t>>;

https://godbolt.org/z/Wh31Pvcjh

  • Related