5#ifndef UGMISC_INT_FINDER_HPP
6#define UGMISC_INT_FINDER_HPP
64#include "ugmisc/templated_callable_loop.hpp"
72enum int_sign { INVALID_SIGN = 0, UNSIGNED = 1, SIGNED = 2 };
77constexpr int_sign flip( int_sign s ) {
78 return static_cast<int_sign
>(((unsigned)s) ^ 3);
90 SELECT_SIGN_OPTION_NONE = 0,
102 SELECT_SIGN_OPTION_MASK = 7,
117 case SELECT_SIGN_OPTION_NONE:
120 return "SELECT_UNSIGNED";
122 return "SELECT_SIGNED";
124 return "SELECT_TYPE_SIGN";
126 return "SELECT_VALUE_SIGN";
137constexpr int_sign sign(sign_opt opt, T v) {
140 case SELECT_UNSIGNED:
return UNSIGNED;
141 case SELECT_SIGNED:
return SIGNED;
142 case SELECT_TYPE_SIGN:
143 return std::is_signed_v<T> ? SIGNED : UNSIGNED;
144 case SELECT_VALUE_SIGN:
145 return v < (T)0 ? SIGNED : UNSIGNED;
158 SELECT_SIGN_FLAGS_NONE,
181constexpr sign_flag operator~(sign_flag f) {
182 return static_cast<sign_flag
>((~(std::uint8_t)f)&(std::uint8_t)SELECT_SIGN_FLAGS_MASK);
187 return static_cast<sign_flag>((std::uint8_t)a | (std::uint8_t)b);
196constexpr bool allow_value_sign_loss(
sign_flag s) {
209 bool smol = smallest(flags);
210 bool signloss = allow_value_sign_loss(flags);
211 if ( smol && signloss ) {
212 return "SELECT_SIGN_SMALLEST|SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS";
214 return "SELECT_SIGN_SMALLEST";
215 }
else if ( signloss ) {
216 return "SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS";
231constexpr bool is_opt = std::is_convertible_v<T, sign_opt>;
234constexpr bool is_flag = std::is_convertible_v<T, sign_flag>;
237constexpr bool is_opt_or_flag = is_opt<T> || is_flag<T>;
240constexpr bool is_int = std::numeric_limits<T>::is_integer;
243constexpr bool is_required_int_tparam_type =
247template<
class T,
class R=T>
248using required_int_tparam_type_only =
250 is_required_int_tparam_type<T>,
257#ifdef UGMISC_USE_CONCEPT_DECLS
259concept Opt = is_opt<T>;
262concept Flag = is_flag<T>;
265concept OptOrFlag = Opt<T> || Flag<T>;
268concept RequiredIntTparamType = OptOrFlag<T> || Int<T>;
279#ifdef UGMISC_USE_CONCEPT_DECLS
280template< RequiredIntTparamType T >
281constexpr sign_opt sign_opt_or_not(T t)
286 -> required_int_tparam_type_only<T, sign_opt>
289 if constexpr ( is_opt<T> ) {
292 return SELECT_SIGN_OPTION_NONE;
297template<
class T>
constexpr bool is_non_null_opt(T t) {
298 return (
unsigned)sign_opt_or_not(t);
302#ifdef UGMISC_USE_CONCEPT_DECLS
303template<RequiredIntTparamType T>
constexpr sign_flag sign_flag_or_not(T t)
305template<
class T>
constexpr auto sign_flag_or_not(T t)
306 -> required_int_tparam_type_only<T, sign_flag>
309 if constexpr ( is_flag<T> ) {
311 }
else if constexpr ( is_opt<T> ) {
312 return SELECT_SIGN_FLAGS_NONE;
316 "sign_flag_or_not takes sign_flag and sign_opt, "
317 "and types convertible to them."
331constexpr sign_opt combine_sign_opts_f()
333 constexpr sign_opt opt_array[
sizeof...(Opts)] = { sign_opt_or_not(Opts)... };
334 sign_opt found_opt = SELECT_SIGN_OPTION_NONE;
335 for( sign_opt opt: opt_array ) {
336 if ( opt == SELECT_SIGN_OPTION_NONE ) {
339 if ( found_opt != SELECT_SIGN_OPTION_NONE && opt != found_opt ) {
340 return SELECT_SIGN_OPTION_NONE;
350template<
auto...Opts>
static constexpr sign_opt combined_sign_opts
351 = combine_sign_opts_f<Opts...>();
354template<
auto...Flags>
static constexpr sign_flag combined_sign_flags
355 = (SELECT_SIGN_FLAGS_NONE | ... | sign_flag_or_not(Flags));
361static constexpr bool valid_sign_opts = (unsigned)combined_sign_opts<Opt...>;
367static constexpr bool any_sign_opts = (... || is_non_null_opt(Opt));
380class flag_opt_combo {
384 flag_opt_combo(
const flag_opt_combo&) =
default;
385 constexpr flag_opt_combo(sign_flag sflags) : m_flags(sflags) {}
387 constexpr sign_flag flags()
const {
return m_flags; }
388 constexpr sign_opt safe_opt()
const {
return SELECT_SIGN_OPTION_NONE; }
392 "This flag_opt_combo does not have an option."
396 constexpr bool has_opt()
const {
return false; }
398 constexpr operator sign_flag()
const {
return flags(); }
403class flag_opt_combo<true> :
public flag_opt_combo<false> {
407 flag_opt_combo(
const flag_opt_combo&) =
default;
408 constexpr flag_opt_combo(sign_opt sopt, sign_flag sflags)
409 : flag_opt_combo<false>(sflags), m_opt(sopt)
412 constexpr sign_opt opt()
const {
return m_opt; }
413 constexpr sign_opt safe_opt()
const {
return opt(); }
414 constexpr bool has_opt()
const {
return true; }
416 constexpr operator sign_opt()
const {
return opt(); }
423constexpr auto combine_flags_opts()
425 constexpr sign_flag flags = combined_sign_flags<args...>;
426 constexpr sign_opt opt = combined_sign_opts<args...>;
427 constexpr bool valid_opts = valid_sign_opts<args...>;
428 constexpr bool any_opts = any_sign_opts<args...>;
430 if constexpr ( any_opts ) {
431 static_assert( valid_opts );
432 return flag_opt_combo<true>{opt, flags};
434 return flag_opt_combo<false>{flags};
456template<
auto...FlagOpts>
457constexpr auto combined = intfind_::combine_flags_opts<FlagOpts...>();
462#ifndef UGMISC_INT_FINDER_MAX_INT_WIDTH
467# define UGMISC_INT_FINDER_MAX_INT_WIDTH (1U << 12);
492 using unsigned_type = std::uint8_t;
493 using signed_type = std::int8_t;
496template<>
struct get_int_types<16> {
497 using unsigned_type = std::uint16_t;
498 using signed_type = std::int16_t;
502#ifndef UGMISC_TEST_INT_FINDER_SKIP_U32
503 using unsigned_type = std::uint32_t;
505 using signed_type = std::int32_t;
509 using unsigned_type = std::uint64_t;
510 using signed_type = std::int64_t;
541constexpr bool check_member_int_types(get_int_types<N> t) {
543 "Integer width must be no more than "
547 using T =
decltype(t);
549 if constexpr ( member_type_access<utype, T>::has_member ) {
550 using I =
typename T::unsigned_type;
551 using limits = std::numeric_limits<I>;
552 constexpr unsigned bits = limits::digits;
553 static_assert( limits::radix == 2 );
554 static_assert( limits::is_integer );
555 static_assert( ! limits::is_signed );
556 static_assert( bits == N );
559 if constexpr ( member_type_access<stype, T>::has_member ) {
560 using I =
typename T::signed_type;
561 using limits = std::numeric_limits<I>;
562 constexpr unsigned bits = limits::digits + 1;
563 static_assert( limits::radix == 2 );
564 static_assert( limits::is_integer );
565 static_assert( limits::is_signed );
566 static_assert( bits == N );
579template<
unsigned N>
struct get_int_types :
public ::ugmisc::get_int_types<N> {
581 static constexpr bool _check =
582 check_member_int_types(::ugmisc::get_int_types<N>{});
608template<
unsigned N,
int_sign Sign>
652template<
unsigned N,
int_sign S>
struct get_exact_int_type;
656struct get_exact_int_type<N, UNSIGNED> {
661struct get_exact_int_type<N, SIGNED> {
675 typename intfind_::get_exact_int_type<N, S>::type;
686static constexpr unsigned find_inner_loop_max = 100;
694template<
int_sign Sign>
697 constexpr decltype(
auto)
operator() (
698 std::integral_constant<unsigned, V> I
704 return template_find_continue;
726template<
unsigned N,
int_sign Sign>
729 intfind_::template_callable_find<N, max_int_width, intfind_::type_finder<Sign>>()
738template<
unsigned N,
int_sign Sign>
740 intfind_::template_callable_find_success<N, max_int_width, intfind_::type_finder<Sign>>();
778#ifdef UGMISC_USE_CONCEPT_DECLS
779template<
bool IncludingSignBit, Int T >
782template<
bool IncludingSignBit,
class T >
787 constexpr bool use_sign_bit = std::is_signed_v<T> && IncludingSignBit;
788 constexpr unsigned extra_bits = use_sign_bit ? 1 : 0;
789 const U w = v >= 0 ? (U)v : ~(U)v;
805template<
unsigned LeastBits,
int_sign Sign>
806constexpr auto find_type() {
807 if constexpr ( has_sufficient_int_type<LeastBits, Sign> ) {
808 using int_type = least_int_type<LeastBits, Sign>;
819struct range_result_compare {
821 range_result_compare(
const range_result_compare&) =
default;
822 constexpr range_result_compare(T v) : value(v) {}
823 constexpr bool valid()
const {
return value; }
824 constexpr unsigned size()
const {
return sizeof(T); }
825 constexpr unsigned used_bits()
const {
826 unsigned sign_bit = std::numeric_limits<T>::is_signed ? 1 : 0;
827 return std::numeric_limits<T>::digits + sign_bit;
838template<
class T,
class U>
839constexpr bool operator < (range_result_compare<T> a, range_result_compare<U> b) {
840 if ( !a.valid() ) {
return false; }
841 if ( !b.valid() ) {
return true; }
842 if ( a.size() < b.size() ) {
return true; }
843 if ( a.size() > b.size() ) {
return false; }
844 return a.used_bits() < b.used_bits();
852 constexpr plus_max(T v) : value(v) {}
853 plus_max(
const plus_max&) =
default;
855 constexpr operator T()
const {
return value; }
859constexpr plus_max<T>
operator+(plus_max<T> a, plus_max<T> b) {
860 return std::max(a.value, b.value);
864plus_max(T v) -> plus_max<T>;
866plus_max(T& v) -> plus_max<T>;
868plus_max(
const T& v) -> plus_max<T>;
874struct get_int_type_from_sequence;
876template<
class T, T...Int>
877struct get_int_type_from_sequence<std::integer_sequence<T, Int...>> {
882using int_type_from_sequence =
typename get_int_type_from_sequence<T>::type;
887struct required_int_arg_traits {
890 ERROR_SIGN_OPT_CLASH,
897 SELECT_SIGN_OPTION_NONE,
898 SELECT_SIGN_OPTION_NONE
901 sign_opt opt = SELECT_SIGN_OPTION_NONE;
902 sign_flag flags = SELECT_SIGN_FLAGS_NONE;
903 bool any_invalid_sign =
false;
904 bool any_signed =
false;
905 bool any_negative =
false;
908 required_int_arg_traits() =
default;
909 required_int_arg_traits(
const required_int_arg_traits&) =
default;
912 constexpr required_int_arg_traits(T v) {
913 if constexpr ( intfind_::is_int<T> ) {
914 any_signed = std::numeric_limits<T>::is_signed;
915 any_negative = v < (T)0;
916 bitwidth = signed_bitwidth<true>(v);
918 if constexpr ( intfind_::is_opt<T> ) { opt = v; }
919 if constexpr ( intfind_::is_flag<T> ) { flags = v; }
925 return ugmisc::sign(opt, any_negative ? -1 : 1);
927 return ugmisc::sign(opt, any_negative ? -1U : 1U);
931 constexpr bool complete()
const {
932 return opt != SELECT_SIGN_OPTION_NONE;
935 constexpr operator bool()
const {
return error == OK; }
941constexpr required_int_arg_traits
operator+(
942 required_int_arg_traits lhs,
943 required_int_arg_traits rhs
946 required_int_arg_traits out{lhs};
950 && lhs.opt != SELECT_SIGN_OPTION_NONE
951 && rhs.opt != SELECT_SIGN_OPTION_NONE
954 out.clashing_opts[0] = lhs.opt;
955 out.clashing_opts[1] = rhs.opt;
956 out.error = required_int_arg_traits::ERROR_INVALID_SIGN;
960 out.opt = lhs.opt == SELECT_SIGN_OPTION_NONE ? rhs.opt : lhs.opt;
961 out.flags = lhs.flags | rhs.flags;
962 out.any_signed = lhs.any_signed || rhs.any_signed;
963 out.any_negative = lhs.any_negative || rhs.any_negative;
964 out.bitwidth = std::max(lhs.bitwidth, rhs.bitwidth);
965 out.any_invalid_sign = lhs.any_invalid_sign || rhs.any_invalid_sign;
966 if ( out.opt != SELECT_SIGN_OPTION_NONE ) {
967 out.any_invalid_sign =
968 out.any_invalid_sign ||(out.sign() == INVALID_SIGN);
971 if ( out.any_invalid_sign && out.error == out.OK ) {
972 out.error = out.ERROR_INVALID_SIGN;
980constexpr required_int_arg_traits
operator+(required_int_arg_traits a, T b) {
981 return a + required_int_arg_traits{b};
986constexpr required_int_arg_traits
operator+(T a, required_int_arg_traits b) {
987 return required_int_arg_traits{a}, b;
1004#ifdef UGMISC_USE_CONCEPT_DECLS
1006requires (... && intfind_::RequiredIntTparamType<
decltype(V1)>)
1012 static constexpr auto value = V;
1017 static constexpr bool value = intfind_::is_int<
decltype(T::value)>;
1026 wrapped_items<V2...>
1030 struct get_sequences {
1032 using std_sequence = std::integer_sequence<I, (I)U::value...>;
1036 type_list<std::integral_constant<I, (I)U::value>...>;
1040 using sequences =
typename wrapped_ints<V2...>
1041 ::template apply_t<get_sequences>;
1044 static constexpr intfind_::required_int_arg_traits get_traits(T...v) {
1045 return (intfind_::required_int_arg_traits{} + ... + v);
1053 static constexpr auto typed_zero() {
1054 constexpr auto traits = get_traits(V1..., V2...);
1056 if constexpr ( !traits ) {
1059 traits.error != traits.ERROR_SIGN_OPT_CLASH,
1060 "Conflicting sign select options provided."
1063 traits.error != traits.ERROR_INVALID_SIGN,
1064 "Valid sign can't be found with this option for these values."
1070 "At least one argument must be a sign_opt."
1073 constexpr int_sign pref_sign = traits.sign();
1074 constexpr int_sign other_sign = flip(pref_sign);
1076 traits.any_negative ? SIGNED : UNSIGNED;
1077 constexpr bool choose_smaller = smallest(traits.flags);
1078 constexpr unsigned value_bits = traits.bitwidth;
1080 constexpr bool may_be_unsigned =
1081 value_sign == UNSIGNED
1082 || allow_value_sign_loss(traits.flags)
1088 constexpr auto pref_value =
1089 intfind_::find_type<value_bits, pref_sign>();
1090 constexpr auto other_value =
1091 intfind_::find_type<value_bits, other_sign>();
1093 using pref_t = std::remove_const_t<
decltype(pref_value)>;
1094 using other_t = std::remove_const_t<
decltype(other_value)>;
1098 constexpr bool pref_b = std::is_same_v<pref_t, bool> ? pref_value :
true;
1099 constexpr bool other_b = std::is_same_v<other_t, bool> ? other_value :
true;
1101 constexpr bool pref_valid = pref_b && (std::numeric_limits<pref_t>::is_signed || may_be_unsigned);
1107 constexpr bool other_valid = other_b && (std::numeric_limits<other_t>::is_signed || may_be_unsigned) && choose_smaller;
1110 pref_valid || other_valid,
1111 "least_required_type could not find a type that satisfies all "
1116 constexpr bool use_pref = [=]() ->
bool {
1121 if constexpr ( ! other_valid ) {
1124 }
else if constexpr ( ! pref_valid ) {
1127 }
else if constexpr (
1128 choose_smaller && other_valid
1130 intfind_::range_result_compare{other_value} < intfind_::range_result_compare{pref_value}
1142 if constexpr ( use_pref ) {
1145 "This is Larry's fault."
1146 " An earlier static assertion should have failed already."
1152 "This is Larry's fault."
1153 " An earlier static assertion should have failed already."
1167 using type =
decltype(typed_zero<V2...>());
1222 using ints =
typename finder::template
type_list<>;
1223 static_assert( ints::size == 1 );
1225 return int_const::value;
1275 :: template std_sequence<V...>;
Provides constexpr bit counting functions.
constexpr auto bitwidth(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
typename sequences< V2... > ::template type_list< type< V2... > > type_list
typename sequences< V2... > ::template std_sequence< type< V2... > > std_sequence
least_required_int_type_finder< V1..., V2... > finder
decltype(typed_zero< V2... >()) type
@ SELECT_SIGNED
Select a signed type.
@ SELECT_UNSIGNED
Select an unsigned type.
constexpr bool has_unsigned_type
constexpr auto to_least_required_int_type()
least_required_int_type< V... > least_required_int_type_multi
constexpr unsigned signed_bitwidth(T v)
#define UGMISC_INT_FINDER_MAX_INT_WIDTH
constexpr bool has_either_int_type
@ SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS
least_int_type< N, UNSIGNED > least_unsigned_type
typename intfind_::get_int_types< N >::signed_type exact_signed_type
least_int_type< N, SIGNED > least_signed_type
constexpr bool has_sufficient_int_type
typename intfind_::get_int_types< N >::unsigned_type exact_unsigned_type
std::string_view get_name(sign_opt opt)
typename least_required_int_type_finder<> ::template std_sequence< V... > least_required_int_std_sequence
decltype( intfind_::template_callable_find< N, max_int_width, intfind_::type_finder< Sign > >()) least_int_type
int_sign
Used to select signed or unsigned types.
constexpr bool has_int_type
constexpr bool has_both_int_types
typename least_required_int_type_finder<> ::template type_list< V... > least_required_int_type_list
constexpr auto as_least_required_int_type
typename intfind_::get_exact_int_type< N, S >::type exact_int_type
typename least_required_int_type_finder<>::template type< V... > least_required_int_type
constexpr bool has_signed_type
constexpr unsigned max_int_width
Testing for and using named static and non static members of types.
#define UGMISC_DECL_MEMBER_ACCESS(TNAME, NAME)
Utility required by other ugmisc headers.
std::enable_if_t< std::numeric_limits< T >::is_integer, R > int_only
Lists of types which may be used in some of the ugmisc templates where a single type would usually be...
typename typelist_::filter_type_list< Test, T, Mod >::type type_list_filter
constexpr auto operator+(T, U) -> concatenate_type_lists_t< T, U >
typename typelist_::get_type_list_member< I, T >::type type_list_member