24#ifndef UGMISC_INT_FINDER_HPP
25#define UGMISC_INT_FINDER_HPP
82enum int_sign { INVALID_SIGN = 0, UNSIGNED = 1, SIGNED = 2 };
87constexpr int_sign flip( int_sign s ) {
88 return static_cast<int_sign
>(((unsigned)s) ^ 3);
100 SELECT_SIGN_OPTION_NONE = 0,
108 SELECT_SIGN_OPTION_MASK = 7,
114constexpr std::string_view get_name(sign_opt opt) {
116 case SELECT_SIGN_OPTION_NONE:
118 case SELECT_UNSIGNED:
119 return "SELECT_UNSIGNED";
121 return "SELECT_SIGNED";
122 case SELECT_TYPE_SIGN:
123 return "SELECT_TYPE_SIGN";
124 case SELECT_VALUE_SIGN:
125 return "SELECT_VALUE_SIGN";
139 case SELECT_UNSIGNED:
return UNSIGNED;
140 case SELECT_SIGNED:
return SIGNED;
142 return std::is_signed_v<T> ? SIGNED : UNSIGNED;
144 return v < 0 ? SIGNED : UNSIGNED;
157 SELECT_SIGN_FLAGS_NONE,
180constexpr sign_flag operator~(sign_flag f) {
181 return static_cast<sign_flag
>((~(std::uint8_t)f)&(std::uint8_t)SELECT_SIGN_FLAGS_MASK);
186 return static_cast<sign_flag>((std::uint8_t)a | (std::uint8_t)b);
195constexpr bool allow_value_sign_loss(
sign_flag s) {
206constexpr std::string_view get_name(
sign_flag flags) {
207 bool smol = smallest(flags);
208 bool signloss = allow_value_sign_loss(flags);
209 if ( smol && signloss ) {
210 return "SELECT_SIGN_SMALLEST|SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS";
212 return "SELECT_SIGN_SMALLEST";
213 }
else if ( signloss ) {
214 return "SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS";
230inline constexpr bool is_opt = std::is_convertible_v<T, sign_opt>;
233inline constexpr bool is_flag = std::is_convertible_v<T, sign_flag>;
244constexpr sign_opt sign_opt_or_not(T t) {
245 if constexpr ( is_opt<T> ) {
247 }
else if constexpr ( is_flag<T> ) {
248 return SELECT_SIGN_OPTION_NONE;
250 static_assert( false );
255template<
class T>
constexpr bool is_non_null_opt(T t) {
256 return (
unsigned)sign_opt_or_not(t);
260template<
class T>
constexpr sign_flag sign_flag_or_not(T t) {
261 if constexpr ( is_flag<T> ) {
263 }
else if constexpr ( is_opt<T> ) {
264 return SELECT_SIGN_FLAGS_NONE;
268 "sign_flag_or_not takes sign_flag and sign_opt, "
269 "and types convertible to them."
283constexpr sign_opt combine_sign_opts_f()
285 constexpr sign_opt opt_array[
sizeof...(Opts)] = { sign_opt_or_not(Opts)... };
286 sign_opt found_opt = SELECT_SIGN_OPTION_NONE;
287 for( sign_opt opt: opt_array ) {
288 if ( opt == SELECT_SIGN_OPTION_NONE ) {
291 if ( found_opt != SELECT_SIGN_OPTION_NONE && opt != found_opt ) {
292 return SELECT_SIGN_OPTION_NONE;
302template<
auto...Opts>
static constexpr sign_opt combined_sign_opts
303 = combine_sign_opts_f<Opts...>();
306template<
auto...Flags>
static constexpr sign_flag combined_sign_flags
307 = (SELECT_SIGN_FLAGS_NONE | ... | sign_flag_or_not(Flags));
313static constexpr bool valid_sign_opts = (unsigned)combined_sign_opts<Opt...>;
319static constexpr bool any_sign_opts = (... || is_non_null_opt(Opt));
332class flag_opt_combo {
336 flag_opt_combo(
const flag_opt_combo&) =
default;
337 constexpr flag_opt_combo(sign_flag sflags) : m_flags(sflags) {}
339 constexpr sign_flag flags()
const {
return m_flags; }
340 constexpr sign_opt safe_opt()
const {
return SELECT_SIGN_OPTION_NONE; }
344 "This flag_opt_combo does not have an option."
349 constexpr operator sign_flag()
const {
return flags(); }
354class flag_opt_combo<true> :
public flag_opt_combo<false> {
358 flag_opt_combo(
const flag_opt_combo&) =
default;
359 constexpr flag_opt_combo(sign_opt sopt, sign_flag sflags)
360 : flag_opt_combo<false>(sflags), m_opt(sopt)
363 constexpr sign_opt opt()
const {
return m_opt; }
364 constexpr sign_opt safe_opt()
const {
return opt(); }
366 constexpr operator sign_opt()
const {
return opt(); }
373constexpr auto combine_flags_opts()
375 constexpr sign_flag flags = combined_sign_flags<args...>;
376 constexpr sign_opt opt = combined_sign_opts<args...>;
377 constexpr bool valid_opts = valid_sign_opts<args...>;
378 constexpr bool any_opts = any_sign_opts<args...>;
380 if constexpr ( any_opts ) {
381 static_assert( valid_opts );
382 return flag_opt_combo<true>{opt, flags};
384 return flag_opt_combo<false>{flags};
399#ifndef UGMISC_INT_FINDER_MAX_INT_WIDTH
404# define UGMISC_INT_FINDER_MAX_INT_WIDTH (1U << 12);
429 using unsigned_type = std::uint8_t;
430 using signed_type = std::int8_t;
433template<>
struct get_int_types<16> {
434 using unsigned_type = std::uint16_t;
435 using signed_type = std::int16_t;
439#ifndef UGMISC_TEST_INT_FINDER_SKIP_U32
440 using unsigned_type = std::uint32_t;
442 using signed_type = std::int32_t;
446 using unsigned_type = std::uint64_t;
447 using signed_type = std::int64_t;
459constexpr unsigned round_up_to_pow2(
unsigned x) {
461 unsigned lo = hi >> 1;
462 return x ? (lo == x ? lo : hi) : 1;
487constexpr bool check_member_int_types(get_int_types<N> t) {
489 "Integer width must be no more than "
493 using T =
decltype(t);
495 if constexpr ( member_type_access<utype, T>::has_member ) {
496 using I =
typename T::unsigned_type;
497 using limits = std::numeric_limits<I>;
498 constexpr unsigned bits = limits::digits;
499 static_assert( limits::radix == 2 );
500 static_assert( limits::is_integer );
501 static_assert( ! limits::is_signed );
502 static_assert( bits == N );
505 if constexpr ( member_type_access<stype, T>::has_member ) {
506 using I =
typename T::signed_type;
507 using limits = std::numeric_limits<I>;
508 constexpr unsigned bits = limits::digits + 1;
509 static_assert( limits::radix == 2 );
510 static_assert( limits::is_integer );
511 static_assert( limits::is_signed );
512 static_assert( bits == N );
525template<
unsigned N>
struct get_int_types :
public ::ugmisc::get_int_types<N> {
527 static constexpr bool _check =
528 check_member_int_types(::ugmisc::get_int_types<N>{});
554template<
unsigned N,
int_sign Sign>
598template<
unsigned N,
int_sign S>
struct get_exact_int_type;
602struct get_exact_int_type<N, UNSIGNED> {
607struct get_exact_int_type<N, SIGNED> {
621 typename intfind_::get_exact_int_type<N, S>::type;
632#ifdef UGMISC_TEST_INT_FINDER_INNER_LOOP_MAX
633static constexpr unsigned find_inner_loop_max = UGMISC_TEST_INT_FINDER_INNER_LOOP_MAX;
635static constexpr unsigned find_inner_loop_max = 100;
647template<
unsigned Min,
int_sign Sign,
unsigned Max>
648constexpr auto find_type_100() {
649 static_assert( Max - Min < find_inner_loop_max );
651 static_assert( Min <= Max );
652 static_assert( Sign == UNSIGNED || Sign == SIGNED );
656 }
else if constexpr ( Min == Max ) {
659 return find_type_100<Min+1, Sign, Max>();
678template<
unsigned Min,
int_sign Sign,
unsigned Max = max_
int_w
idth>
679constexpr auto find_type() {
681 static_assert(Max >= Min);
682 static_assert( Sign == UNSIGNED || Sign == SIGNED );
684 constexpr unsigned near_max = std::min(Min+find_inner_loop_max-1, Max);
685 constexpr bool last_iter = near_max == Max;
687 if constexpr ( find_type_100<Min, Sign, near_max>() ) {
688 return find_type_100<Min, Sign, near_max>();
689 }
else if constexpr ( last_iter ) {
692 return find_type<near_max+1, Sign, Max>();
712template<
unsigned N,
int_sign S>
715 intfind_::find_type<N, S>(),
716 decltype(intfind_::find_type<N, S>())
755#ifdef UGMISC_USE_CONCEPT_DECLS
756template<
bool IncludingSignBit, Int T >
759template<
bool IncludingSignBit,
class T >
764 constexpr bool use_sign_bit = std::is_signed_v<T> && IncludingSignBit;
765 constexpr unsigned extra_bits = use_sign_bit ? 1 : 0;
766 const U w = v >= 0 ? (U)v : ~(U)v;
780struct range_result_compare {
782 range_result_compare(
const range_result_compare&) =
default;
783 constexpr range_result_compare(T v) : value(v) {}
784 constexpr bool valid()
const {
return value; }
785 constexpr unsigned size()
const {
return sizeof(T); }
786 constexpr unsigned used_bits()
const {
787 unsigned sign_bit = std::numeric_limits<T>::is_signed ? 1 : 0;
788 return std::numeric_limits<T>::digits + sign_bit;
799template<
class T,
class U>
800constexpr bool operator < (range_result_compare<T> a, range_result_compare<U> b) {
801 if ( !a.valid() ) {
return false; }
802 if ( !b.valid() ) {
return true; }
803 if ( a.size() < b.size() ) {
return true; }
804 if ( a.size() > b.size() ) {
return false; }
805 return a.used_bits() < b.used_bits();
811template<
auto V,
auto...SelectArgs>
812constexpr auto as_least_required_type() {
813 constexpr auto S = intfind_::combine_flags_opts<SelectArgs...>();
814 constexpr int_sign pref_sign = sign(S, V);
815 constexpr int_sign other_sign = flip(pref_sign);
816 constexpr int_sign value_sign = V < 0 ? SIGNED : UNSIGNED;
818 constexpr bool may_be_unsigned =
819 value_sign == UNSIGNED
820 || allow_value_sign_loss(S)
823 constexpr unsigned value_bits = signed_bitwidth<true>(V);
824 constexpr bool choose_smaller = smallest(S);
829 constexpr auto pref_value =
830 intfind_::find_type<value_bits, pref_sign>();
831 constexpr auto other_value =
832 intfind_::find_type<value_bits, other_sign>();
834 using pref_t =
decltype(pref_value);
835 using other_t =
decltype(other_value);
837 constexpr bool pref_valid = pref_value && (std::numeric_limits<pref_t>::is_signed || may_be_unsigned);
843 constexpr bool other_valid = other_value && (std::numeric_limits<other_t>::is_signed || may_be_unsigned) && choose_smaller;
846 pref_valid || other_valid,
847 "least_required_type could not find a type that satisfies all "
852 constexpr bool use_pref = [=]() ->
bool {
857 if constexpr ( ! other_valid ) {
860 }
else if constexpr ( ! pref_valid ) {
863 }
else if constexpr (
864 choose_smaller && other_valid
866 range_result_compare{other_value} < range_result_compare{pref_value}
878 if constexpr ( use_pref ) {
881 "This is Larry's fault."
882 " An earlier static assertion should have failed already."
884 return static_cast<pref_t
>(V);
888 "This is Larry's fault."
889 " An earlier static assertion should have failed already."
891 return static_cast<other_t
>(V);
919template<
auto V,
auto...S>
921 intfind_::as_least_required_type<V, S...>();
934template<
auto V,
auto...S>
Provides constexpr bit counting functions.
constexpr auto bitwidth(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:330
sign_opt
Definition int_finder.hpp:99
@ SELECT_VALUE_SIGN
Select a signed type iff the value is negative.
Definition int_finder.hpp:105
@ SELECT_TYPE_SIGN
Definition int_finder.hpp:103
constexpr bool has_unsigned_type
Definition int_finder.hpp:563
decltype(as_least_required_int_type< V, S... >) least_required_int_type
Definition int_finder.hpp:935
constexpr unsigned signed_bitwidth(T v)
Definition int_finder.hpp:757
#define UGMISC_INT_FINDER_MAX_INT_WIDTH
Definition int_finder.hpp:404
constexpr bool has_either_int_type
Definition int_finder.hpp:574
sign_flag
Definition int_finder.hpp:156
@ SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS
Definition int_finder.hpp:174
@ SELECT_SIGN_SMALLEST
Definition int_finder.hpp:167
least_int_type< N, UNSIGNED > least_unsigned_type
Definition int_finder.hpp:724
typename intfind_::get_int_types< N >::signed_type exact_signed_type
Definition int_finder.hpp:584
least_int_type< N, SIGNED > least_signed_type
Definition int_finder.hpp:731
typename intfind_::get_int_types< N >::unsigned_type exact_unsigned_type
Definition int_finder.hpp:581
int_sign
Used to select signed or unsigned types.
Definition int_finder.hpp:82
constexpr auto as_least_required_int_type
Definition int_finder.hpp:920
constexpr bool has_int_type
Definition int_finder.hpp:555
constexpr bool has_both_int_types
Definition int_finder.hpp:570
typename intfind_::get_exact_int_type< N, S >::type exact_int_type
Definition int_finder.hpp:620
constexpr bool has_signed_type
Definition int_finder.hpp:567
std::enable_if_t< intfind_::find_type< N, S >(), decltype(intfind_::find_type< N, S >()) > least_int_type
Definition int_finder.hpp:713
constexpr unsigned max_int_width
Definition int_finder.hpp:411
Testing for and using named static and non static members of types.
#define UGMISC_DECL_MEMBER_ACCESS(TNAME, NAME)
Definition member.hpp:166
#define UGMISC_QQ(...)
Definition quote.hpp:53
std::enable_if_t< std::numeric_limits< T >::is_integer, R > int_only
Definition sfinae_helpers.hpp:235
Definition int_finder.hpp:425
Definition member.hpp:1378