81enum int_sign { INVALID_SIGN = 0, UNSIGNED = 1, SIGNED = 2 };
86constexpr int_sign flip( int_sign s ) {
87 return static_cast<int_sign
>(((unsigned)s) ^ 3);
99 SELECT_SIGN_OPTION_NONE = 0,
107 SELECT_SIGN_OPTION_MASK = 7,
113constexpr std::string_view get_name(sign_opt opt) {
115 case SELECT_SIGN_OPTION_NONE:
117 case SELECT_UNSIGNED:
118 return "SELECT_UNSIGNED";
120 return "SELECT_SIGNED";
121 case SELECT_TYPE_SIGN:
122 return "SELECT_TYPE_SIGN";
123 case SELECT_VALUE_SIGN:
124 return "SELECT_VALUE_SIGN";
138 case SELECT_UNSIGNED:
return UNSIGNED;
139 case SELECT_SIGNED:
return SIGNED;
141 return std::is_signed_v<T> ? SIGNED : UNSIGNED;
143 return v < 0 ? SIGNED : UNSIGNED;
156 SELECT_SIGN_FLAGS_NONE,
179constexpr sign_flag operator~(sign_flag f) {
180 return static_cast<sign_flag
>((~(std::uint8_t)f)&(std::uint8_t)SELECT_SIGN_FLAGS_MASK);
185 return static_cast<sign_flag>((std::uint8_t)a | (std::uint8_t)b);
194constexpr bool allow_value_sign_loss(
sign_flag s) {
205constexpr std::string_view get_name(
sign_flag flags) {
206 bool smol = smallest(flags);
207 bool signloss = allow_value_sign_loss(flags);
208 if ( smol && signloss ) {
209 return "SELECT_SIGN_SMALLEST|SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS";
211 return "SELECT_SIGN_SMALLEST";
212 }
else if ( signloss ) {
213 return "SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS";
229inline constexpr bool is_opt = std::is_convertible_v<T, sign_opt>;
232inline constexpr bool is_flag = std::is_convertible_v<T, sign_flag>;
243constexpr sign_opt sign_opt_or_not(T t) {
244 if constexpr ( is_opt<T> ) {
246 }
else if constexpr ( is_flag<T> ) {
247 return SELECT_SIGN_OPTION_NONE;
249 static_assert( false );
254template<
class T>
constexpr bool is_non_null_opt(T t) {
255 return (
unsigned)sign_opt_or_not(t);
259template<
class T>
constexpr sign_flag sign_flag_or_not(T t) {
260 if constexpr ( is_flag<T> ) {
262 }
else if constexpr ( is_opt<T> ) {
263 return SELECT_SIGN_FLAGS_NONE;
267 "sign_flag_or_not takes sign_flag and sign_opt, "
268 "and types convertible to them."
282constexpr sign_opt combine_sign_opts_f()
284 constexpr sign_opt opt_array[
sizeof...(Opts)] = { sign_opt_or_not(Opts)... };
285 sign_opt found_opt = SELECT_SIGN_OPTION_NONE;
286 for( sign_opt opt: opt_array ) {
287 if ( opt == SELECT_SIGN_OPTION_NONE ) {
290 if ( found_opt != SELECT_SIGN_OPTION_NONE && opt != found_opt ) {
291 return SELECT_SIGN_OPTION_NONE;
301template<
auto...Opts>
static constexpr sign_opt combined_sign_opts
302 = combine_sign_opts_f<Opts...>();
305template<
auto...Flags>
static constexpr sign_flag combined_sign_flags
306 = (SELECT_SIGN_FLAGS_NONE | ... | sign_flag_or_not(Flags));
312static constexpr bool valid_sign_opts = (unsigned)combined_sign_opts<Opt...>;
318static constexpr bool any_sign_opts = (... || is_non_null_opt(Opt));
331class flag_opt_combo {
335 flag_opt_combo(
const flag_opt_combo&) =
default;
336 constexpr flag_opt_combo(sign_flag sflags) : m_flags(sflags) {}
338 constexpr sign_flag flags()
const {
return m_flags; }
339 constexpr sign_opt safe_opt()
const {
return SELECT_SIGN_OPTION_NONE; }
343 "This flag_opt_combo does not have an option."
348 constexpr operator sign_flag()
const {
return flags(); }
353class flag_opt_combo<true> :
public flag_opt_combo<false> {
357 flag_opt_combo(
const flag_opt_combo&) =
default;
358 constexpr flag_opt_combo(sign_opt sopt, sign_flag sflags)
359 : flag_opt_combo<false>(sflags), m_opt(sopt)
362 constexpr sign_opt opt()
const {
return m_opt; }
363 constexpr sign_opt safe_opt()
const {
return opt(); }
365 constexpr operator sign_opt()
const {
return opt(); }
372constexpr auto combine_flags_opts()
374 constexpr sign_flag flags = combined_sign_flags<args...>;
375 constexpr sign_opt opt = combined_sign_opts<args...>;
376 constexpr bool valid_opts = valid_sign_opts<args...>;
377 constexpr bool any_opts = any_sign_opts<args...>;
379 if constexpr ( any_opts ) {
380 static_assert( valid_opts );
381 return flag_opt_combo<true>{opt, flags};
383 return flag_opt_combo<false>{flags};
398#ifndef UGMISC_INT_FINDER_MAX_INT_WIDTH
403# define UGMISC_INT_FINDER_MAX_INT_WIDTH (1U << 12);
428 using unsigned_type = std::uint8_t;
429 using signed_type = std::int8_t;
432template<>
struct get_int_types<16> {
433 using unsigned_type = std::uint16_t;
434 using signed_type = std::int16_t;
438#ifndef UGMISC_TEST_INT_FINDER_SKIP_U32
439 using unsigned_type = std::uint32_t;
441 using signed_type = std::int32_t;
445 using unsigned_type = std::uint64_t;
446 using signed_type = std::int64_t;
458constexpr unsigned round_up_to_pow2(
unsigned x) {
460 unsigned lo = hi >> 1;
461 return x ? (lo == x ? lo : hi) : 1;
486constexpr bool check_member_int_types(get_int_types<N> t) {
488 "Integer width must be no more than "
492 using T =
decltype(t);
494 if constexpr ( has_member_type_unsigned_type<T> ) {
495 using I =
typename T::unsigned_type;
496 using limits = std::numeric_limits<I>;
497 constexpr unsigned bits = limits::digits;
498 static_assert( limits::radix == 2 );
499 static_assert( limits::is_integer );
500 static_assert( ! limits::is_signed );
501 static_assert( bits == N );
504 if constexpr ( has_member_type_signed_type<T> ) {
505 using I =
typename T::signed_type;
506 using limits = std::numeric_limits<I>;
507 constexpr unsigned bits = limits::digits + 1;
508 static_assert( limits::radix == 2 );
509 static_assert( limits::is_integer );
510 static_assert( limits::is_signed );
511 static_assert( bits == N );
524template<
unsigned N>
struct get_int_types :
public ::ugmisc::get_int_types<N> {
526 static constexpr bool _check =
527 check_member_int_types(::ugmisc::get_int_types<N>{});
553template<
unsigned N,
int_sign Sign>
556 ? intfind_::has_member_type_unsigned_type< intfind_::get_int_types<N> >
557 : intfind_::has_member_type_signed_type< intfind_::get_int_types<N> >
597template<
unsigned N,
int_sign S>
struct get_exact_int_type;
601struct get_exact_int_type<N, UNSIGNED> {
606struct get_exact_int_type<N, SIGNED> {
620 typename intfind_::get_exact_int_type<N, S>::type;
631#ifdef UGMISC_TEST_INT_FINDER_INNER_LOOP_MAX
632static constexpr unsigned find_inner_loop_max = UGMISC_TEST_INT_FINDER_INNER_LOOP_MAX;
634static constexpr unsigned find_inner_loop_max = 100;
646template<
unsigned Min,
int_sign Sign,
unsigned Max>
647constexpr auto find_type_100() {
648 static_assert( Max - Min < find_inner_loop_max );
650 static_assert( Min <= Max );
651 static_assert( Sign == UNSIGNED || Sign == SIGNED );
655 }
else if constexpr ( Min == Max ) {
658 return find_type_100<Min+1, Sign, Max>();
677template<
unsigned Min,
int_sign Sign,
unsigned Max = max_
int_w
idth>
678constexpr auto find_type() {
680 static_assert(Max >= Min);
681 static_assert( Sign == UNSIGNED || Sign == SIGNED );
683 constexpr unsigned near_max = std::min(Min+find_inner_loop_max-1, Max);
684 constexpr bool last_iter = near_max == Max;
686 if constexpr ( find_type_100<Min, Sign, near_max>() ) {
687 return find_type_100<Min, Sign, near_max>();
688 }
else if constexpr ( last_iter ) {
691 return find_type<near_max+1, Sign, Max>();
711template<
unsigned N,
int_sign S>
714 intfind_::find_type<N, S>(),
715 decltype(intfind_::find_type<N, S>())
754template<
bool IncludingSignBit,
class T >
757 constexpr bool use_sign_bit = std::is_signed_v<T> && IncludingSignBit;
758 constexpr unsigned extra_bits = use_sign_bit ? 1 : 0;
759 const U w = v >= 0 ? (U)v : ~(U)v;
773struct range_result_compare {
775 range_result_compare(
const range_result_compare&) =
default;
776 constexpr range_result_compare(T v) : value(v) {}
777 constexpr bool valid()
const {
return value; }
778 constexpr unsigned size()
const {
return sizeof(T); }
779 constexpr unsigned used_bits()
const {
780 unsigned sign_bit = std::numeric_limits<T>::is_signed ? 1 : 0;
781 return std::numeric_limits<T>::digits + sign_bit;
792template<
class T,
class U>
793constexpr bool operator < (range_result_compare<T> a, range_result_compare<U> b) {
794 if ( !a.valid() ) {
return false; }
795 if ( !b.valid() ) {
return true; }
796 if ( a.size() < b.size() ) {
return true; }
797 if ( a.size() > b.size() ) {
return false; }
798 return a.used_bits() < b.used_bits();
804template<
auto V,
auto...SelectArgs>
805constexpr auto as_least_required_type() {
806 constexpr auto S = intfind_::combine_flags_opts<SelectArgs...>();
807 constexpr int_sign pref_sign = sign(S, V);
808 constexpr int_sign other_sign = flip(pref_sign);
809 constexpr int_sign value_sign = V < 0 ? SIGNED : UNSIGNED;
811 constexpr bool may_be_unsigned =
812 value_sign == UNSIGNED
813 || allow_value_sign_loss(S)
816 constexpr unsigned value_bits = signed_bitwidth<true>(V);
817 constexpr bool choose_smaller = smallest(S);
822 constexpr auto pref_value =
823 intfind_::find_type<value_bits, pref_sign>();
824 constexpr auto other_value =
825 intfind_::find_type<value_bits, other_sign>();
827 using pref_t =
decltype(pref_value);
828 using other_t =
decltype(other_value);
830 constexpr bool pref_valid = pref_value && (std::numeric_limits<pref_t>::is_signed || may_be_unsigned);
836 constexpr bool other_valid = other_value && (std::numeric_limits<other_t>::is_signed || may_be_unsigned) && choose_smaller;
839 pref_valid || other_valid,
840 "least_required_type could not find a type that satisfies all "
845 constexpr bool use_pref = [=]() ->
bool {
850 if constexpr ( ! other_valid ) {
853 }
else if constexpr ( ! pref_valid ) {
856 }
else if constexpr (
857 choose_smaller && other_valid
859 range_result_compare{other_value} < range_result_compare{pref_value}
871 if constexpr ( use_pref ) {
874 "This is Larry's fault."
875 " An earlier static assertion should have failed already."
877 return static_cast<pref_t
>(V);
881 "This is Larry's fault."
882 " An earlier static assertion should have failed already."
884 return static_cast<other_t
>(V);
912template<
auto V,
auto...S>
914 intfind_::as_least_required_type<V, S...>();
927template<
auto V,
auto...S>
Provides constexpr bit counting functions.
constexpr bitwise_uint_only< T > bitwidth(T) noexcept
Definition bitops.hpp:235
sign_opt
Definition int_finder.hpp:98
@ SELECT_VALUE_SIGN
Select a signed type iff the value is negative.
Definition int_finder.hpp:104
@ SELECT_TYPE_SIGN
Definition int_finder.hpp:102
typename intfind_::get_exact_int_type< N, S >::type exact_int_type
Definition int_finder.hpp:619
std::enable_if_t< intfind_::find_type< N, S >(), decltype(intfind_::find_type< N, S >()) > least_int_type
Definition int_finder.hpp:712
constexpr bool has_unsigned_type
Definition int_finder.hpp:562
decltype(as_least_required_int_type< V, S... >) least_required_int_type
Definition int_finder.hpp:928
#define UGMISC_INT_FINDER_MAX_INT_WIDTH
Definition int_finder.hpp:403
constexpr bool has_either_int_type
Definition int_finder.hpp:573
sign_flag
Definition int_finder.hpp:155
@ SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS
Definition int_finder.hpp:173
@ SELECT_SIGN_SMALLEST
Definition int_finder.hpp:166
least_int_type< N, UNSIGNED > least_unsigned_type
Definition int_finder.hpp:723
typename intfind_::get_int_types< N >::signed_type exact_signed_type
Definition int_finder.hpp:583
least_int_type< N, SIGNED > least_signed_type
Definition int_finder.hpp:730
typename intfind_::get_int_types< N >::unsigned_type exact_unsigned_type
Definition int_finder.hpp:580
constexpr int_only< T, unsigned > signed_bitwidth(T v)
Definition int_finder.hpp:755
int_sign
Used to select signed or unsigned types.
Definition int_finder.hpp:81
constexpr auto as_least_required_int_type
Definition int_finder.hpp:913
constexpr bool has_int_type
Definition int_finder.hpp:554
constexpr bool has_both_int_types
Definition int_finder.hpp:569
constexpr bool has_signed_type
Definition int_finder.hpp:566
constexpr unsigned max_int_width
Definition int_finder.hpp:410
Testing for and using named static and non static members of types.
#define UGMISC_MEMBER_TYPE_TEST(NAME)
Definition member.hpp:481
#define UGMISC_QQ(...)
Definition quote.hpp:52
std::enable_if_t< std::numeric_limits< T >::is_integer, R > int_only
Definition sfinae_helpers.hpp:157
Definition int_finder.hpp:424