Reputation: 933
For an own implementation of std::bitset
(std forbidden) I used uint_fast32_t
, since it's faster on 64bit CPUs. A review remark was to save space for small sets, e.g. bitset<6> shouldn't use 8 bytes. The waste is even more if you consider alignment in a struct.
To use C++11 is fine. I would like to elegantly select:
as storage type in my class:
template<size_t Size>
struct BitSet {
typedef <expression> StorageType;
// an array of that storage type...
};
I can just think of rather clumsy helper templates, but maybe there is something more elegant in C++11.
Edit: to clarify: uint_fast8_t
would be fine for the original class, the compiler may choose whatever is fast. Imagine Size==1000000. But it would be 64 bit on some machines, and when size matters in some use-cases, e.g. Size==4 it means 7 bytes would be wasted.
Upvotes: 2
Views: 1579
Reputation: 1
Simple solution:
template<std::size_t MAX_VAL>
using min_unsigned_type = std::tuple_element_t<
bit_width(MAX_VAL), // See bellow.
std::tuple<
std::uint8_t,
std::uint16_t,
std::uint32_t,
std::uint64_t
>>;
This is simply using the bit_width
, ie. the minimum number of bits that can fit the maximum value needed to index the std::tuple
of all the types you want to use.
C++20 has a std::bit_width
that is constexpr
, but if needed it's easy to write your own version.
Upvotes: 0
Reputation: 18631
You can also make use of constexpr if
if your compiler supports it - might be the easiest one to read. Example based on code from here:
template<size_t S>
static constexpr auto _findStorageType() {
static_assert(S > 0, "Invalid storage size");
if constexpr (S <= sizeof(uint8_t) * CHAR_BIT) return uint8_t{};
else if constexpr (S <= sizeof(uint16_t) * CHAR_BIT) return uint16_t{};
else if constexpr (S <= sizeof(uint32_t) * CHAR_BIT) return uint32_t{};
else if constexpr (S <= sizeof(uint64_t) * CHAR_BIT) return uint64_t{};
else return __uint128_t{};
}
Upvotes: 0
Reputation: 238401
You can use a recursive template class with a non-type template argument for the bit limit, and a member type alias for the corresponding integer type.
You don't need to write such template yourself though, Boost has you covered: boost::uint_t<N>::least
(or boost::uint_t<N>::fast
if you're not looking for the smallest, but fastest).
P.S. If you plan to implement the template yourself, and you want the smallest integer type (as per the question in the title), then you should use std::uint_leastN_t
instead of uint_fastN_t
or uintN_t
. The former is not necessarily the smallest type, and the latter is not guaranteed to exist on all systems.
Furthermore uint_fast32_t
is not guaranteed to be able to represent more bits than 32, so it is a rather poor choice for Size > 32
. I would recommend uint_least64_t
instead.
Upvotes: 1
Reputation: 27684
A verbose solution with less magic. Ref: https://gcc.godbolt.org/z/P2AGZ8
#include<cstdint>
namespace helper { namespace internal {
enum class Size {
Uint8,
Uint16,
Uint32,
UintFast32,
};
constexpr Size get_size(int s) {
return (s <= 8 ) ? Size::Uint8:
(s <= 16) ? Size::Uint16:
(s <= 32) ? Size::Uint32: Size::UintFast32;
}
template<Size s>
struct FindTypeH;
template<>
struct FindTypeH<Size::Uint8> {
using Type = std::uint8_t;
};
template<>
struct FindTypeH<Size::Uint16> {
using Type = std::uint16_t;
};
template<>
struct FindTypeH<Size::Uint32> {
using Type = std::uint32_t;
};
template<>
struct FindTypeH<Size::UintFast32> {
using Type = std::uint_fast32_t;
};
}
template<int S>
struct FindType {
using Type = typename internal::FindTypeH<internal::get_size(S)>::Type;
};
}
template<int S>
struct Myclass {
using Type = typename helper::FindType<S>::Type;
};
Upvotes: 0
Reputation: 96579
Given the small selection of possible types, I'd just hardcode the conditions:
using StorageType = std::conditional_t<Size <= 8, uint8_t,
std::conditional_t<Size <= 16, uint16_t,
std::conditional_t<Size <= 32, uint32_t, uint_fast32_t>>>;
Alternatively, using a trick similar to one from @Caleth's answer:
using StorageType = std::tuple_element_t<(Size > 8) + (Size > 16) + (Size > 32),
std::tuple<uint8_t, uint16_t, uint32_t, uint_fast32_t>>;
Upvotes: 10
Reputation: 63019
With a helper
namespace detail {
template <int overload>
struct StorageType;
template <>
struct StorageType<0>{ using type = uint8_t; };
template <>
struct StorageType<1>{ using type = uint16_t; };
template <>
struct StorageType<2>{ using type = uint32_t; };
template <>
struct StorageType<3>{ using type = uint_fast32_t; };
}
We can then sum constexpr
booleans
template<size_t Size>
struct BitSet {
typedef typename detail::StorageType<(Size > 8) + (Size > 16) + (Size > 32)>::type StorageType;
// an array of that storage type...
};
Upvotes: 4