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>>;