5858 // to the 'include std' path
5959 #if defined(CPP2_IMPORT_STD) && defined(__cpp_lib_modules)
6060 import std.compat;
61+ #include < cerrno>
6162 // If 'include std' was requested, include all standard headers.
6263 // This list tracks the current draft standard, so as of this
6364 // writing includes draft C++26 headers like <debugging>.
254255 #endif
255256 #include < algorithm>
256257 #include < any>
258+ #include < cerrno>
257259 #include < compare>
258260 #include < concepts>
259261 #include < cstddef>
@@ -451,6 +453,144 @@ using _schar = signed char; // normally use i8 instead
451453using _uchar = unsigned char ; // normally use u8 instead
452454
453455
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+
454594// -----------------------------------------------------------------------
455595//
456596// String utilities
@@ -557,6 +697,7 @@ constexpr bool is_escaped(std::string_view s) {
557697}
558698
559699inline bool string_to_int (std::string const & s, int & v, int base = 10 ) {
700+ #ifdef CPP2_NO_EXCEPTIONS
560701 try {
561702 v = stoi (s, nullptr , base);
562703 return true ;
@@ -569,6 +710,32 @@ inline bool string_to_int(std::string const& s, int& v, int base = 10) {
569710 {
570711 return false ;
571712 }
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
572739}
573740
574741template <int Base = 10 >
@@ -922,94 +1089,6 @@ template<class T, class U>
9221089}
9231090
9241091
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-
10131092namespace impl {
10141093
10151094template <class T > struct dependent_false : std::false_type {};
@@ -1405,7 +1484,7 @@ class deferred_init {
14051484public:
14061485 constexpr deferred_init () noexcept { }
14071486 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 (); }
14091488
14101489 constexpr auto construct (auto && ...args) -> void { cpp2_default.enforce (!init); new (&data) T{CPP2_FORWARD (args)...}; init = true ; }
14111490};
@@ -1762,22 +1841,6 @@ constexpr auto is( X const& x, bool (*value)(X const&) ) -> bool {
17621841// The 'as' cast functions are <To, From> so use that order here
17631842// If it's confusing, we can switch this to <From, To>
17641843
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-
17811844template < typename To, typename From >
17821845constexpr auto is_unsafe_pointer_conversion_v =
17831846 std::is_pointer_v<To>
@@ -2183,35 +2246,6 @@ class finally_presuccess
21832246};
21842247
21852248
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-
22152249// -----------------------------------------------------------------------
22162250//
22172251// args: see main() arguments as a container of string_views
@@ -2328,9 +2362,9 @@ class range
23282362 if (include_last) {
23292363 if constexpr (std::integral<TT>) {
23302364 if (last == std::numeric_limits<TT>::max ()) {
2331- throw std::runtime_error (
2365+ impl::Throw ( std::runtime_error (
23322366 " range with last == numeric_limits<T>::max() will overflow"
2333- );
2367+ ), " range with last == numeric_limits<T>::max() will overflow " ) ;
23342368 }
23352369 }
23362370 ++last;
0 commit comments