58
58
// to the 'include std' path
59
59
#if defined(CPP2_IMPORT_STD) && defined(__cpp_lib_modules)
60
60
import std.compat;
61
+ #include < cerrno>
61
62
// If 'include std' was requested, include all standard headers.
62
63
// This list tracks the current draft standard, so as of this
63
64
// writing includes draft C++26 headers like <debugging>.
254
255
#endif
255
256
#include < algorithm>
256
257
#include < any>
258
+ #include < cerrno>
257
259
#include < compare>
258
260
#include < concepts>
259
261
#include < cstddef>
@@ -451,6 +453,144 @@ using _schar = signed char; // normally use i8 instead
451
453
using _uchar = unsigned char ; // normally use u8 instead
452
454
453
455
456
+ // -----------------------------------------------------------------------
457
+ //
458
+ // An implementation of GSL's narrow_cast with a clearly 'unchecked' name
459
+ //
460
+ // -----------------------------------------------------------------------
461
+ //
462
+ namespace impl {
463
+
464
+ template < typename To, typename From >
465
+ constexpr auto is_narrowing_v =
466
+ // [dcl.init.list] 7.1
467
+ (std::is_floating_point_v<From> && std::is_integral_v<To>) ||
468
+ // [dcl.init.list] 7.2
469
+ (std::is_floating_point_v<From> && std::is_floating_point_v<To> && sizeof (From) > sizeof (To)) || // NOLINT(misc-redundant-expression)
470
+ // [dcl.init.list] 7.3
471
+ (std::is_integral_v<From> && std::is_floating_point_v<To>) ||
472
+ (std::is_enum_v<From> && std::is_floating_point_v<To>) ||
473
+ // [dcl.init.list] 7.4
474
+ (std::is_integral_v<From> && std::is_integral_v<To> && sizeof (From) > sizeof (To)) || // NOLINT(misc-redundant-expression)
475
+ (std::is_enum_v<From> && std::is_integral_v<To> && sizeof (From) > sizeof (To)) ||
476
+ // [dcl.init.list] 7.5
477
+ (std::is_pointer_v<From> && std::is_same_v<To, bool >)
478
+ ;
479
+
480
+ }
481
+
482
+
483
+ template <typename C, typename X>
484
+ constexpr auto unchecked_narrow ( X x ) noexcept
485
+ -> decltype(auto )
486
+ requires (
487
+ impl::is_narrowing_v<C, X>
488
+ || (
489
+ std::is_arithmetic_v<C>
490
+ && std::is_arithmetic_v<X>
491
+ )
492
+ )
493
+ {
494
+ return static_cast <C>(x);
495
+ }
496
+
497
+
498
+ template <typename C, typename X>
499
+ constexpr auto unchecked_cast ( X&& x ) noexcept
500
+ -> decltype(auto )
501
+ {
502
+ return static_cast <C>(CPP2_FORWARD (x));
503
+ }
504
+
505
+
506
+ // -----------------------------------------------------------------------
507
+ //
508
+ // contract_group
509
+ //
510
+ // -----------------------------------------------------------------------
511
+ //
512
+
513
+ #ifdef CPP2_USE_SOURCE_LOCATION
514
+ #define CPP2_SOURCE_LOCATION_PARAM , [[maybe_unused]] std::source_location where
515
+ #define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT , [[maybe_unused]] std::source_location where = std::source_location::current()
516
+ #define CPP2_SOURCE_LOCATION_PARAM_SOLO [[maybe_unused]] std::source_location where
517
+ #define CPP2_SOURCE_LOCATION_ARG , where
518
+ #define CPP2_SOURCE_LOCATION_VALUE (cpp2::to_string(where.file_name()) + " (" + cpp2::to_string(where.line()) + " ) " + where.function_name())
519
+ #else
520
+ #define CPP2_SOURCE_LOCATION_PARAM
521
+ #define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT
522
+ #define CPP2_SOURCE_LOCATION_PARAM_SOLO
523
+ #define CPP2_SOURCE_LOCATION_ARG
524
+ #define CPP2_SOURCE_LOCATION_VALUE std::string (" " )
525
+ #endif
526
+
527
+ // For C++23: make this std::string_view and drop the macro
528
+ // Before C++23 std::string_view was not guaranteed to be trivially copyable,
529
+ // and so in<T> will pass it by const& and really it should be by value
530
+ #define CPP2_MESSAGE_PARAM char const *
531
+ #define CPP2_CONTRACT_MSG cpp2::message_to_cstr_adapter
532
+
533
+ inline auto message_to_cstr_adapter ( CPP2_MESSAGE_PARAM msg ) -> CPP2_MESSAGE_PARAM { return msg ? msg : " " ; }
534
+ inline auto message_to_cstr_adapter ( std::string const & msg ) -> CPP2_MESSAGE_PARAM { return msg.c_str (); }
535
+
536
+ class contract_group {
537
+ public:
538
+ using handler = void (*)(CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM);
539
+
540
+ constexpr contract_group (handler h = {}) : reporter{h} { }
541
+ constexpr auto set_handler (handler h = {}) { reporter = h; }
542
+ constexpr auto is_active () const -> bool { return reporter != handler{}; }
543
+
544
+ constexpr auto enforce (bool b, CPP2_MESSAGE_PARAM msg = " " CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
545
+ -> void { if (!b) report_violation (msg CPP2_SOURCE_LOCATION_ARG); }
546
+ constexpr auto report_violation (CPP2_MESSAGE_PARAM msg = " " CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
547
+ -> void { if (reporter) reporter (msg CPP2_SOURCE_LOCATION_ARG); }
548
+ private:
549
+ handler reporter;
550
+ };
551
+
552
+ [[noreturn]] inline auto report_and_terminate (std::string_view group, CPP2_MESSAGE_PARAM msg = " " CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) noexcept -> void {
553
+ std::cerr
554
+ #ifdef CPP2_USE_SOURCE_LOCATION
555
+ << where.file_name () << " ("
556
+ << where.line () << " ) "
557
+ << where.function_name () << " : "
558
+ #endif
559
+ << group << " violation" ;
560
+ if (msg && msg[0 ] != ' \0 ' ) {
561
+ std::cerr << " : " << msg;
562
+ }
563
+ std::cerr << " \n " ;
564
+ std::terminate ();
565
+ }
566
+
567
+ auto inline cpp2_default = contract_group(
568
+ [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
569
+ report_and_terminate (" Contract" , msg CPP2_SOURCE_LOCATION_ARG);
570
+ }
571
+ );
572
+ auto inline bounds_safety = contract_group(
573
+ [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
574
+ report_and_terminate (" Bounds safety" , msg CPP2_SOURCE_LOCATION_ARG);
575
+ }
576
+ );
577
+ auto inline null_safety = contract_group(
578
+ [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
579
+ report_and_terminate (" Null safety" , msg CPP2_SOURCE_LOCATION_ARG);
580
+ }
581
+ );
582
+ auto inline type_safety = contract_group(
583
+ [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
584
+ report_and_terminate (" Type safety" , msg CPP2_SOURCE_LOCATION_ARG);
585
+ }
586
+ );
587
+ auto inline testing = contract_group(
588
+ [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
589
+ report_and_terminate (" Testing" , msg CPP2_SOURCE_LOCATION_ARG);
590
+ }
591
+ );
592
+
593
+
454
594
// -----------------------------------------------------------------------
455
595
//
456
596
// String utilities
@@ -557,6 +697,7 @@ constexpr bool is_escaped(std::string_view s) {
557
697
}
558
698
559
699
inline bool string_to_int (std::string const & s, int & v, int base = 10 ) {
700
+ #ifdef CPP2_NO_EXCEPTIONS
560
701
try {
561
702
v = stoi (s, nullptr , base);
562
703
return true ;
@@ -569,6 +710,32 @@ inline bool string_to_int(std::string const& s, int& v, int base = 10) {
569
710
{
570
711
return false ;
571
712
}
713
+ #else
714
+ errno = 0 ;
715
+ char * end = nullptr ;
716
+
717
+ auto const num = std::strtol (s.c_str (), &end, base);
718
+
719
+ cpp2_default.enforce (end != nullptr );
720
+ if (
721
+ end == s.c_str ()
722
+ || *end != ' \0 '
723
+ )
724
+ {
725
+ return false ; // invalid argument
726
+ }
727
+ if (
728
+ errno == ERANGE
729
+ || num < std::numeric_limits<int >::min ()
730
+ || num > std::numeric_limits<int >::max ()
731
+ )
732
+ {
733
+ return false ; // out of range
734
+ }
735
+
736
+ v = unchecked_narrow<int >(num);
737
+ return true ;
738
+ #endif
572
739
}
573
740
574
741
template <int Base = 10 >
@@ -922,94 +1089,6 @@ template<class T, class U>
922
1089
}
923
1090
924
1091
925
- // -----------------------------------------------------------------------
926
- //
927
- // contract_group
928
- //
929
- // -----------------------------------------------------------------------
930
- //
931
-
932
- #ifdef CPP2_USE_SOURCE_LOCATION
933
- #define CPP2_SOURCE_LOCATION_PARAM , [[maybe_unused]] std::source_location where
934
- #define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT , [[maybe_unused]] std::source_location where = std::source_location::current()
935
- #define CPP2_SOURCE_LOCATION_PARAM_SOLO [[maybe_unused]] std::source_location where
936
- #define CPP2_SOURCE_LOCATION_ARG , where
937
- #define CPP2_SOURCE_LOCATION_VALUE (cpp2::to_string(where.file_name()) + " (" + cpp2::to_string(where.line()) + " ) " + where.function_name())
938
- #else
939
- #define CPP2_SOURCE_LOCATION_PARAM
940
- #define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT
941
- #define CPP2_SOURCE_LOCATION_PARAM_SOLO
942
- #define CPP2_SOURCE_LOCATION_ARG
943
- #define CPP2_SOURCE_LOCATION_VALUE std::string (" " )
944
- #endif
945
-
946
- // For C++23: make this std::string_view and drop the macro
947
- // Before C++23 std::string_view was not guaranteed to be trivially copyable,
948
- // and so in<T> will pass it by const& and really it should be by value
949
- #define CPP2_MESSAGE_PARAM char const *
950
- #define CPP2_CONTRACT_MSG cpp2::message_to_cstr_adapter
951
-
952
- inline auto message_to_cstr_adapter ( CPP2_MESSAGE_PARAM msg ) -> CPP2_MESSAGE_PARAM { return msg ? msg : " " ; }
953
- inline auto message_to_cstr_adapter ( std::string const & msg ) -> CPP2_MESSAGE_PARAM { return msg.c_str (); }
954
-
955
- class contract_group {
956
- public:
957
- using handler = void (*)(CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM);
958
-
959
- constexpr contract_group (handler h = {}) : reporter{h} { }
960
- constexpr auto set_handler (handler h = {}) { reporter = h; }
961
- constexpr auto is_active () const -> bool { return reporter != handler{}; }
962
-
963
- constexpr auto enforce (bool b, CPP2_MESSAGE_PARAM msg = " " CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
964
- -> void { if (!b) report_violation (msg CPP2_SOURCE_LOCATION_ARG); }
965
- constexpr auto report_violation (CPP2_MESSAGE_PARAM msg = " " CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT)
966
- -> void { if (reporter) reporter (msg CPP2_SOURCE_LOCATION_ARG); }
967
- private:
968
- handler reporter;
969
- };
970
-
971
- [[noreturn]] inline auto report_and_terminate (std::string_view group, CPP2_MESSAGE_PARAM msg = " " CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) noexcept -> void {
972
- std::cerr
973
- #ifdef CPP2_USE_SOURCE_LOCATION
974
- << where.file_name () << " ("
975
- << where.line () << " ) "
976
- << where.function_name () << " : "
977
- #endif
978
- << group << " violation" ;
979
- if (msg && msg[0 ] != ' \0 ' ) {
980
- std::cerr << " : " << msg;
981
- }
982
- std::cerr << " \n " ;
983
- std::terminate ();
984
- }
985
-
986
- auto inline cpp2_default = contract_group(
987
- [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
988
- report_and_terminate (" Contract" , msg CPP2_SOURCE_LOCATION_ARG);
989
- }
990
- );
991
- auto inline bounds_safety = contract_group(
992
- [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
993
- report_and_terminate (" Bounds safety" , msg CPP2_SOURCE_LOCATION_ARG);
994
- }
995
- );
996
- auto inline null_safety = contract_group(
997
- [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
998
- report_and_terminate (" Null safety" , msg CPP2_SOURCE_LOCATION_ARG);
999
- }
1000
- );
1001
- auto inline type_safety = contract_group(
1002
- [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
1003
- report_and_terminate (" Type safety" , msg CPP2_SOURCE_LOCATION_ARG);
1004
- }
1005
- );
1006
- auto inline testing = contract_group(
1007
- [](CPP2_MESSAGE_PARAM msg CPP2_SOURCE_LOCATION_PARAM)noexcept {
1008
- report_and_terminate (" Testing" , msg CPP2_SOURCE_LOCATION_ARG);
1009
- }
1010
- );
1011
-
1012
-
1013
1092
namespace impl {
1014
1093
1015
1094
template <class T > struct dependent_false : std::false_type {};
@@ -1405,7 +1484,7 @@ class deferred_init {
1405
1484
public:
1406
1485
constexpr deferred_init () noexcept { }
1407
1486
constexpr ~deferred_init () noexcept { destroy (); }
1408
- constexpr auto value () noexcept -> T& { cpp2_default.enforce (init); return t (); }
1487
+ constexpr auto value () noexcept -> T& { cpp2_default.enforce (init); return t (); }
1409
1488
1410
1489
constexpr auto construct (auto && ...args) -> void { cpp2_default.enforce (!init); new (&data) T{CPP2_FORWARD (args)...}; init = true ; }
1411
1490
};
@@ -1762,22 +1841,6 @@ constexpr auto is( X const& x, bool (*value)(X const&) ) -> bool {
1762
1841
// The 'as' cast functions are <To, From> so use that order here
1763
1842
// If it's confusing, we can switch this to <From, To>
1764
1843
1765
- template < typename To, typename From >
1766
- constexpr auto is_narrowing_v =
1767
- // [dcl.init.list] 7.1
1768
- (std::is_floating_point_v<From> && std::is_integral_v<To>) ||
1769
- // [dcl.init.list] 7.2
1770
- (std::is_floating_point_v<From> && std::is_floating_point_v<To> && sizeof (From) > sizeof (To)) || // NOLINT(misc-redundant-expression)
1771
- // [dcl.init.list] 7.3
1772
- (std::is_integral_v<From> && std::is_floating_point_v<To>) ||
1773
- (std::is_enum_v<From> && std::is_floating_point_v<To>) ||
1774
- // [dcl.init.list] 7.4
1775
- (std::is_integral_v<From> && std::is_integral_v<To> && sizeof (From) > sizeof (To)) || // NOLINT(misc-redundant-expression)
1776
- (std::is_enum_v<From> && std::is_integral_v<To> && sizeof (From) > sizeof (To)) ||
1777
- // [dcl.init.list] 7.5
1778
- (std::is_pointer_v<From> && std::is_same_v<To, bool >)
1779
- ;
1780
-
1781
1844
template < typename To, typename From >
1782
1845
constexpr auto is_unsafe_pointer_conversion_v =
1783
1846
std::is_pointer_v<To>
@@ -2183,35 +2246,6 @@ class finally_presuccess
2183
2246
};
2184
2247
2185
2248
2186
- // -----------------------------------------------------------------------
2187
- //
2188
- // An implementation of GSL's narrow_cast with a clearly 'unchecked' name
2189
- //
2190
- // -----------------------------------------------------------------------
2191
- //
2192
- template <typename C, typename X>
2193
- constexpr auto unchecked_narrow ( X x ) noexcept
2194
- -> decltype(auto )
2195
- requires (
2196
- impl::is_narrowing_v<C, X>
2197
- || (
2198
- std::is_arithmetic_v<C>
2199
- && std::is_arithmetic_v<X>
2200
- )
2201
- )
2202
- {
2203
- return static_cast <C>(x);
2204
- }
2205
-
2206
-
2207
- template <typename C, typename X>
2208
- constexpr auto unchecked_cast ( X&& x ) noexcept
2209
- -> decltype(auto )
2210
- {
2211
- return static_cast <C>(CPP2_FORWARD (x));
2212
- }
2213
-
2214
-
2215
2249
// -----------------------------------------------------------------------
2216
2250
//
2217
2251
// args: see main() arguments as a container of string_views
@@ -2328,9 +2362,9 @@ class range
2328
2362
if (include_last) {
2329
2363
if constexpr (std::integral<TT>) {
2330
2364
if (last == std::numeric_limits<TT>::max ()) {
2331
- throw std::runtime_error (
2365
+ impl::Throw ( std::runtime_error (
2332
2366
" range with last == numeric_limits<T>::max() will overflow"
2333
- );
2367
+ ), " range with last == numeric_limits<T>::max() will overflow " ) ;
2334
2368
}
2335
2369
}
2336
2370
++last;
0 commit comments