UgMisc 0.3
Miscellaneous C++ header library
Loading...
Searching...
No Matches
int_finder.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: © 2025 Larry Chips <larry@larrychips.net>
3 * SPDX-Licence-Identifier: MIT
4 */
5#ifndef UGMISC_INT_FINDER_HPP
6#define UGMISC_INT_FINDER_HPP
7
54
55#include <cstdint>
56#include <limits>
57#include <string_view>
58#include <type_traits>
59#include "ugmisc/bitops.hpp"
60#include "ugmisc/features.hpp"
61#include "ugmisc/member.hpp"
62#include "ugmisc/quote.hpp"
64#include "ugmisc/templated_callable_loop.hpp"
65#include "ugmisc/typelist.hpp"
66
67
68namespace ugmisc {
69
70
72enum int_sign { INVALID_SIGN = 0, UNSIGNED = 1, SIGNED = 2 };
73
74
75
76
77constexpr int_sign flip( int_sign s ) {
78 return static_cast<int_sign>(((unsigned)s) ^ 3);
79}
80
81
82
83
89enum sign_opt : std::uint8_t {
90 SELECT_SIGN_OPTION_NONE = 0,
100
101 SELECT_SIGN_OPTION_MAX = SELECT_VALUE_SIGN,
102 SELECT_SIGN_OPTION_MASK = 7,
103};
104
105
106
107
115inline std::string_view get_name(sign_opt opt) {
116 switch( opt ) {
117 case SELECT_SIGN_OPTION_NONE:
118 return "NONE";
119 case SELECT_UNSIGNED:
120 return "SELECT_UNSIGNED";
121 case SELECT_SIGNED:
122 return "SELECT_SIGNED";
123 case SELECT_TYPE_SIGN:
124 return "SELECT_TYPE_SIGN";
126 return "SELECT_VALUE_SIGN";
127 default:
128 return "INVALID";
129 }
130};
131
132
133
134
135
136template<class T>
137constexpr int_sign sign(sign_opt opt, T v) {
138
139 switch( opt ) {
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;
146 default:
147 return INVALID_SIGN;
148 }
149}
150
151
152
153
157enum sign_flag : std::uint8_t {
158 SELECT_SIGN_FLAGS_NONE,
169
176
178};
179
180
181constexpr sign_flag operator~(sign_flag f) {
182 return static_cast<sign_flag>((~(std::uint8_t)f)&(std::uint8_t)SELECT_SIGN_FLAGS_MASK);
183}
184
185
186constexpr sign_flag operator|(sign_flag a, sign_flag b) {
187 return static_cast<sign_flag>((std::uint8_t)a | (std::uint8_t)b);
188}
189
190
191constexpr bool smallest(sign_flag s) {
192 return (std::uint8_t)SELECT_SIGN_SMALLEST & (std::uint8_t)s;
193}
194
195
196constexpr bool allow_value_sign_loss(sign_flag s) {
197 return (std::uint8_t)SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS & (std::uint8_t)s;
198}
199
200
201/*
202 * This might have to be a `string` instead of a `string_view` if we add more
203 * flags, because we can't just special case every combination.
204 *
205 * This function is *not* declared `constexpr` in case it one day has to return
206 * `std::string`.
207 */
208inline std::string_view get_name(sign_flag flags) {
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";
213 } else if ( smol ) {
214 return "SELECT_SIGN_SMALLEST";
215 } else if ( signloss ) {
216 return "SELECT_SIGN_ALLOW_VALUE_SIGN_LOSS";
217 } else {
218 return "NONE";
219 }
220}
221
222
223
224
226namespace intfind_ {
227
228
229
230template<class T>
231constexpr bool is_opt = std::is_convertible_v<T, sign_opt>;
232
233template<class T>
234constexpr bool is_flag = std::is_convertible_v<T, sign_flag>;
235
236template<class T>
237constexpr bool is_opt_or_flag = is_opt<T> || is_flag<T>;
238
239template<class T>
240constexpr bool is_int = std::numeric_limits<T>::is_integer;
241
242template<class T>
243constexpr bool is_required_int_tparam_type =
244 is_opt_or_flag<T>
245 || is_int<T>;
246
247template<class T, class R=T>
248using required_int_tparam_type_only =
249 std::enable_if_t<
250 is_required_int_tparam_type<T>,
251 R
252 >;
253
254/*
255 * Concepts will make errors more understandable, if available.
256 */
257#ifdef UGMISC_USE_CONCEPT_DECLS
258template<class T>
259concept Opt = is_opt<T>;
260
261template<class T>
262concept Flag = is_flag<T>;
263
264template<class T>
265concept OptOrFlag = Opt<T> || Flag<T>;
266
267template<class T>
268concept RequiredIntTparamType = OptOrFlag<T> || Int<T>;
269#endif
270
271
272
273
279#ifdef UGMISC_USE_CONCEPT_DECLS
280template< RequiredIntTparamType T >
281constexpr sign_opt sign_opt_or_not(T t)
282#else
283template< class T >
284constexpr auto
285sign_opt_or_not(T t)
286 -> required_int_tparam_type_only<T, sign_opt>
287#endif
288{
289 if constexpr ( is_opt<T> ) {
290 return static_cast<sign_opt>(t);
291 } else {
292 return SELECT_SIGN_OPTION_NONE;
293 }
294}
295
296
297template<class T> constexpr bool is_non_null_opt(T t) {
298 return (unsigned)sign_opt_or_not(t);
299}
300
301
302#ifdef UGMISC_USE_CONCEPT_DECLS
303template<RequiredIntTparamType T> constexpr sign_flag sign_flag_or_not(T t)
304#else
305template<class T> constexpr auto sign_flag_or_not(T t)
306 -> required_int_tparam_type_only<T, sign_flag>
307#endif
308{
309 if constexpr ( is_flag<T> ) {
310 return static_cast<sign_flag>(t);
311 } else if constexpr ( is_opt<T> ) {
312 return SELECT_SIGN_FLAGS_NONE;
313 } else {
314 static_assert(
315 false,
316 "sign_flag_or_not takes sign_flag and sign_opt, "
317 "and types convertible to them."
318 );
319 }
320}
321
322
323
324
330template<auto...Opts>
331constexpr sign_opt combine_sign_opts_f()
332{
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 ) {
337 continue;
338 }
339 if ( found_opt != SELECT_SIGN_OPTION_NONE && opt != found_opt ) {
340 return SELECT_SIGN_OPTION_NONE;
341 }
342 found_opt = opt;
343 }
344 return found_opt;
345}
346
347
348
349
350template<auto...Opts> static constexpr sign_opt combined_sign_opts
351 = combine_sign_opts_f<Opts...>();
352
353
354template<auto...Flags> static constexpr sign_flag combined_sign_flags
355 = (SELECT_SIGN_FLAGS_NONE | ... | sign_flag_or_not(Flags));
356
357
358
359
360template<auto...Opt>
361static constexpr bool valid_sign_opts = (unsigned)combined_sign_opts<Opt...>;
362
363
364
365
366template<auto...Opt>
367static constexpr bool any_sign_opts = (... || is_non_null_opt(Opt));
368
369
370
371
379template<bool HasOpt>
380class flag_opt_combo {
381 const sign_flag m_flags;
382
383public:
384 flag_opt_combo(const flag_opt_combo&) = default;
385 constexpr flag_opt_combo(sign_flag sflags) : m_flags(sflags) {}
386
387 constexpr sign_flag flags() const { return m_flags; }
388 constexpr sign_opt safe_opt() const { return SELECT_SIGN_OPTION_NONE; }
389 constexpr sign_opt opt() const {
390 static_assert(
391 false,
392 "This flag_opt_combo does not have an option."
393 );
394 return safe_opt();
395 }
396 constexpr bool has_opt() const { return false; }
397
398 constexpr operator sign_flag() const { return flags(); }
399};
400
401
402template<>
403class flag_opt_combo<true> : public flag_opt_combo<false> {
404 const sign_opt m_opt;
405
406public:
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)
410 {}
411
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; }
415
416 constexpr operator sign_opt() const { return opt(); }
417};
418
419
420
421
422template<auto...args>
423constexpr auto combine_flags_opts()
424{
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...>;
429
430 if constexpr ( any_opts ) {
431 static_assert( valid_opts );
432 return flag_opt_combo<true>{opt, flags};
433 } else {
434 return flag_opt_combo<false>{flags};
435 }
436}
437
438
439
440
441
442} /* intfind_ */
444// INTERNAL
445
446
447
448
456template<auto...FlagOpts>
457constexpr auto combined = intfind_::combine_flags_opts<FlagOpts...>();
458
459
460
461
462#ifndef UGMISC_INT_FINDER_MAX_INT_WIDTH
467# define UGMISC_INT_FINDER_MAX_INT_WIDTH (1U << 12);
468#endif
475
476
477
478
488template<unsigned N> struct get_int_types {};
489
490
491template<> struct get_int_types<8> {
492 using unsigned_type = std::uint8_t;
493 using signed_type = std::int8_t;
494};
495
496template<> struct get_int_types<16> {
497 using unsigned_type = std::uint16_t;
498 using signed_type = std::int16_t;
499};
500
501template<> struct get_int_types<32> {
502#ifndef UGMISC_TEST_INT_FINDER_SKIP_U32
503 using unsigned_type = std::uint32_t;
504#endif
505 using signed_type = std::int32_t;
506};
507
508template<> struct get_int_types<64> {
509 using unsigned_type = std::uint64_t;
510 using signed_type = std::int64_t;
511};
512
513
514
515
517namespace intfind_ {
518
519
520
521
522/*
523 * This allows us to test the existence of a type easily.
524 *
525 * Resulting declarations are listed below for clarity.
526 */
527UGMISC_DECL_MEMBER_ACCESS(utype, unsigned_type);
528UGMISC_DECL_MEMBER_ACCESS(stype, signed_type);
529// has_member_type_unsigned_type< T >; // T can be a type_list.
530// member_type_unsigned_type< T, D (optional) > // T can be type_list,
531 // D is a default type.
532// member_type_test_unsigned_type< T > // Usually not used directly.
533// has_member_type_signed_type< T >; // T can be a type_list.
534// member_type_signed_type< T, D (optional) > // T can be type_list,
535 // D is a default type.
536// member_type_test_signed_type< T > // Usually not used directly.
537
538
539
540template<unsigned N>
541constexpr bool check_member_int_types(get_int_types<N> t) {
542 static_assert( N <= max_int_width,
543 "Integer width must be no more than "
545 " bits."
546 );
547 using T = decltype(t);
548
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 );
557 }
558
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; // Sign bit.
563 static_assert( limits::radix == 2 );
564 static_assert( limits::is_integer );
565 static_assert( limits::is_signed );
566 static_assert( bits == N );
567 }
568
569 return true;
570}
571
572
573
574
579template<unsigned N> struct get_int_types : public ::ugmisc::get_int_types<N> {
580private:
581 static constexpr bool _check =
582 check_member_int_types(::ugmisc::get_int_types<N>{});
583};
584
585
586
587
588} /* intfind_ (::ugmisc::intfind_) */
590// INTERNAL
591
592
593
594
608template<unsigned N, int_sign Sign>
609inline constexpr bool has_int_type =
610 Sign == UNSIGNED
613 ;
614
615
617template<unsigned N> inline constexpr bool has_unsigned_type =
619
621template<unsigned N> inline constexpr bool has_signed_type = has_int_type<N, SIGNED>;
622
624template<unsigned N> inline constexpr bool has_both_int_types =
626
628template<unsigned N> inline constexpr bool has_either_int_type =
630
631
632
633
635template<unsigned N> using exact_unsigned_type = typename intfind_::get_int_types<N>::unsigned_type;
636
638template<unsigned N> using exact_signed_type = typename intfind_::get_int_types<N>::signed_type;
639
640
641
642
644namespace intfind_ {
645
646
652template<unsigned N, int_sign S> struct get_exact_int_type;
653
654
655template<unsigned N>
656struct get_exact_int_type<N, UNSIGNED> {
657 using type = typename get_int_types<N>::unsigned_type;
658};
659
660template<unsigned N>
661struct get_exact_int_type<N, SIGNED> {
662 using type = typename get_int_types<N>::signed_type;
663};
664
665
666} /* intfind_ */
668// INTERNAL
669
670
674template<unsigned N, int_sign S> using exact_int_type =
675 typename intfind_::get_exact_int_type<N, S>::type;
676
677
678
679
681namespace intfind_ {
682
683
684
685
686static constexpr unsigned find_inner_loop_max = 100;
687
688
689
690
694template<int_sign Sign>
695struct type_finder {
696 template<unsigned V>
697 constexpr decltype(auto) operator() (
698 std::integral_constant<unsigned, V> I
699 ) const
700 {
701 if constexpr ( has_int_type<I.value, Sign> ) {
702 return exact_int_type<I.value, Sign>{1};
703 } else {
704 return template_find_continue;
705 }
706 }
707};
708
709
710
711
712}
714// INTERNAL
715
716
717
718
726template<unsigned N, int_sign Sign>
728 decltype(
729 intfind_::template_callable_find<N, max_int_width, intfind_::type_finder<Sign>>()
730 );
731
732
738template<unsigned N, int_sign Sign>
740 intfind_::template_callable_find_success<N, max_int_width, intfind_::type_finder<Sign>>();
741
742
746template<unsigned N>
748
749
753template<unsigned N>
755
756
757
758
778#ifdef UGMISC_USE_CONCEPT_DECLS
779template< bool IncludingSignBit, Int T >
780constexpr unsigned signed_bitwidth(T v)
781#else
782template< bool IncludingSignBit, class T >
784#endif
785{
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;
790 return bitwidth(w) + extra_bits;
791}
792
793
794
795
797namespace intfind_ {
798
799
800
801
802/*
803 * Returns `int_type{1}` if there is such a type, otherwise returns `false`.
804 */
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>;
809 return int_type{1};
810 } else {
811 return false;
812 }
813}
814
815
816
817
818template<class T>
819struct range_result_compare {
820 const T value;
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;
828 }
829};
830
831
832
833
834/*
835 * When looking for a smaller result type, a valid one always wins if the other
836 * is invalid. We care most about storage size, then used bits.
837 */
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();
845}
846
847
848
849template<class T>
850struct plus_max {
851 T value;
852 constexpr plus_max(T v) : value(v) {}
853 plus_max(const plus_max&) = default;
854
855 constexpr operator T() const { return value; }
856};
857
858template<class T>
859constexpr plus_max<T> operator+(plus_max<T> a, plus_max<T> b) {
860 return std::max(a.value, b.value);
861}
862
863template<class T>
864plus_max(T v) -> plus_max<T>;
865template<class T>
866plus_max(T& v) -> plus_max<T>;
867template<class T>
868plus_max(const T& v) -> plus_max<T>;
869
870
871
872
873template<class T>
874struct get_int_type_from_sequence;
875
876template<class T, T...Int>
877struct get_int_type_from_sequence<std::integer_sequence<T, Int...>> {
878 using type = T;
879};
880
881template<class T>
882using int_type_from_sequence = typename get_int_type_from_sequence<T>::type;
883
884
885
886
887struct required_int_arg_traits {
888 enum errcode {
889 OK,
890 ERROR_SIGN_OPT_CLASH,
891 ERROR_INVALID_SIGN
892 };
893
894 errcode error = OK;
895
896 sign_opt clashing_opts[2] = {
897 SELECT_SIGN_OPTION_NONE,
898 SELECT_SIGN_OPTION_NONE
899 };
900
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;
906 unsigned bitwidth = 0;
907
908 required_int_arg_traits() = default;
909 required_int_arg_traits(const required_int_arg_traits&) = default;
910
911 template<class T>
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);
917 } else {
918 if constexpr ( intfind_::is_opt<T> ) { opt = v; }
919 if constexpr ( intfind_::is_flag<T> ) { flags = v; }
920 }
921 }
922
923 constexpr int_sign sign() const {
924 if ( any_signed ) {
925 return ugmisc::sign(opt, any_negative ? -1 : 1);
926 } else {
927 return ugmisc::sign(opt, any_negative ? -1U : 1U);
928 }
929 }
930
931 constexpr bool complete() const {
932 return opt != SELECT_SIGN_OPTION_NONE;
933 }
934
935 constexpr operator bool() const { return error == OK; }
936};
937
938
939
940
941constexpr required_int_arg_traits operator+(
942 required_int_arg_traits lhs,
943 required_int_arg_traits rhs
944 )
945{
946 required_int_arg_traits out{lhs};
947
948 if (
949 lhs.opt != rhs.opt
950 && lhs.opt != SELECT_SIGN_OPTION_NONE
951 && rhs.opt != SELECT_SIGN_OPTION_NONE
952 )
953 {
954 out.clashing_opts[0] = lhs.opt;
955 out.clashing_opts[1] = rhs.opt;
956 out.error = required_int_arg_traits::ERROR_INVALID_SIGN;
957 return out;
958 }
959
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);
969 }
970
971 if ( out.any_invalid_sign && out.error == out.OK ) {
972 out.error = out.ERROR_INVALID_SIGN;
973 }
974
975 return out;
976}
977
978
979template<class T>
980constexpr required_int_arg_traits operator+(required_int_arg_traits a, T b) {
981 return a + required_int_arg_traits{b};
982}
983
984
985template<class T>
986constexpr required_int_arg_traits operator+(T a, required_int_arg_traits b) {
987 return required_int_arg_traits{a}, b;
988}
989
990
991
992
993} /* intfind_ (::ugmisc::intfind_) */
995// INTERNAL
996
997
998
999
1003template<auto...V1>
1004#ifdef UGMISC_USE_CONCEPT_DECLS
1005// Try to make errors more self-explanatory.
1006requires (... && intfind_::RequiredIntTparamType<decltype(V1)>)
1007#endif
1009{
1010 template<auto V>
1011 struct wrapped {
1012 static constexpr auto value = V;
1013 };
1014
1015 template<class T>
1016 struct int_test {
1017 static constexpr bool value = intfind_::is_int<decltype(T::value)>;
1018 };
1019
1020 template<auto...V2>
1021 using wrapped_items = type_list<wrapped<V1>..., wrapped<V2>...>;
1022
1023 template<auto...V2>
1024 using wrapped_ints = type_list_filter<
1025 int_test,
1026 wrapped_items<V2...>
1027 >;
1028
1029 template<class...U>
1030 struct get_sequences {
1031 template<class I>
1032 using std_sequence = std::integer_sequence<I, (I)U::value...>;
1033
1034 template<class I>
1035 using type_list =
1036 type_list<std::integral_constant<I, (I)U::value>...>;
1037 };
1038
1039 template<auto...V2>
1040 using sequences = typename wrapped_ints<V2...>
1041 ::template apply_t<get_sequences>;
1042
1043 template<class...T>
1044 static constexpr intfind_::required_int_arg_traits get_traits(T...v) {
1045 return (intfind_::required_int_arg_traits{} + ... + v);
1046 }
1047
1048 /*
1049 * Returns std::integer_sequence<T, (T)V...>, where T is the required type
1050 * to hold all the values, according to the requirements of SelectArgs.
1051 */
1052 template<auto...V2>
1053 static constexpr auto typed_zero() {
1054 constexpr auto traits = get_traits(V1..., V2...);
1055
1056 if constexpr ( !traits ) {
1057 // Error in traits.
1058 static_assert(
1059 traits.error != traits.ERROR_SIGN_OPT_CLASH,
1060 "Conflicting sign select options provided."
1061 );
1062 static_assert(
1063 traits.error != traits.ERROR_INVALID_SIGN,
1064 "Valid sign can't be found with this option for these values."
1065 );
1066 }
1067
1068 static_assert(
1069 traits.complete(),
1070 "At least one argument must be a sign_opt."
1071 );
1072
1073 constexpr int_sign pref_sign = traits.sign();
1074 constexpr int_sign other_sign = flip(pref_sign);
1075 constexpr int_sign value_sign =
1076 traits.any_negative ? SIGNED : UNSIGNED;
1077 constexpr bool choose_smaller = smallest(traits.flags);
1078 constexpr unsigned value_bits = traits.bitwidth;
1079
1080 constexpr bool may_be_unsigned =
1081 value_sign == UNSIGNED
1082 || allow_value_sign_loss(traits.flags)
1083 ;
1084
1085 // pref_value is a value which converts to true in boolean context if a
1086 // type was found, and whose type (in that case) is the type found.
1087 // Otherwise it will be false, and its type will be bool.
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>();
1092
1093 using pref_t = std::remove_const_t<decltype(pref_value)>;
1094 using other_t = std::remove_const_t<decltype(other_value)>;
1095
1096 // We want to be compatible with custom int types that don't
1097 // automatically convert to bool (although most probably do).
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;
1100
1101 constexpr bool pref_valid = pref_b && (std::numeric_limits<pref_t>::is_signed || may_be_unsigned);
1102
1103 /*
1104 * We never treat the sign select option as a mere suggestion unless
1105 * allowing a smaller type of the other sign to be chosen.
1106 */
1107 constexpr bool other_valid = other_b && (std::numeric_limits<other_t>::is_signed || may_be_unsigned) && choose_smaller;
1108
1109 static_assert(
1110 pref_valid || other_valid,
1111 "least_required_type could not find a type that satisfies all "
1112 "requirements."
1113 );
1114
1115
1116 constexpr bool use_pref = [=]() -> bool {
1117 // Find out whether to use the found type (if any) of the preferred
1118 // sign, or the other one. Usually there will always be a pair of
1119 // types of one size available, signed and unsigned, so most of these
1120 // cases will not be matched.
1121 if constexpr ( ! other_valid ) {
1122 // We have a valid type of preferred sign, and no type of other sign.
1123 return true;
1124 } else if constexpr ( ! pref_valid ) {
1125 // No preferred sign available, but the other sign is available.
1126 return false;
1127 } else if constexpr (
1128 choose_smaller && other_valid
1129 &&
1130 intfind_::range_result_compare{other_value} < intfind_::range_result_compare{pref_value}
1131 )
1132 {
1133 // There is a smaller type available that is large enough but not
1134 // of the preferred type.
1135 return false;
1136 } else {
1137 // Special cases have been exhausted (probably not really, sorry).
1138 return true;
1139 }
1140 }();
1141
1142 if constexpr ( use_pref ) {
1143 static_assert(
1144 pref_valid,
1145 "This is Larry's fault."
1146 " An earlier static assertion should have failed already."
1147 );
1148 return (pref_t)0;
1149 } else {
1150 static_assert(
1151 other_valid,
1152 "This is Larry's fault."
1153 " An earlier static assertion should have failed already."
1154 );
1155 return (other_t)0;
1156 }
1157 }
1158
1159public:
1166 template<auto...V2>
1167 using type = decltype(typed_zero<V2...>());
1168
1174 template<auto...V2>
1175 using std_sequence = typename sequences<V2...>
1176 ::template std_sequence<type<V2...>>;
1177
1183 template<auto...V2>
1184 using type_list = typename sequences<V2...>
1185 ::template type_list<type<V2...>>;
1186
1206 template<auto...V2>
1208};
1209
1210
1211
1212
1219template<auto...V>
1221 using finder = least_required_int_type_finder<V...>;
1222 using ints = typename finder::template type_list<>;
1223 static_assert( ints::size == 1 );
1224 using int_const = type_list_member<0, ints>;
1225 return int_const::value;
1226};
1227
1228
1229
1230
1234template<auto...V>
1235inline constexpr auto as_least_required_int_type =
1237
1238
1239
1240
1253template<auto...V>
1256
1257
1262template<auto...V>
1264
1265
1272template<auto...V>
1275 :: template std_sequence<V...>;
1276
1277
1284template<auto...V>
1287 ::template type_list<V...>;
1288
1289
1290
1291
1292} /* ugmisc */
1293#endif /* UGMISC_INT_FINDER_HPP */
Provides constexpr bit counting functions.
constexpr auto bitwidth(T) noexcept -> UGMISC_BITWISE_UINT_RETURN(T, int)
Definition bitops.hpp:317
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
Feature detection.
@ SELECT_SIGNED
Select a signed type.
@ SELECT_UNSIGNED
Select an unsigned type.
@ SELECT_VALUE_SIGN
@ SELECT_TYPE_SIGN
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
@ SELECT_SIGN_SMALLEST
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
constexpr auto combined
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)
Definition member.hpp:148
#define UGMISC_QQ(...)
Definition quote.hpp:34
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
Definition typelist.hpp:365
constexpr auto operator+(T, U) -> concatenate_type_lists_t< T, U >
typename typelist_::get_type_list_member< I, T >::type type_list_member
Definition typelist.hpp:195