Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Exceptions.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_Exceptions_h_
5#define _Stroika_Foundation_Execution_Exceptions_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <cerrno>
10#include <exception>
11#include <mutex>
12#include <optional>
13#include <stdexcept>
14#include <string>
15#include <system_error>
16
18
19#include "Stroika/Foundation/Execution/Activity.h"
20#include "Stroika/Foundation/Execution/Throw.h"
21
22/**
23 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
24 *
25 * \note \em Design Note
26 * (essentially) All exceptions thrown by Stroika (except where needed by quirks of underlying library
27 * integrated with) inherit from std::exception, or are Stroika::Foundation::Execution::SilentException.
28 *
29 * This means that any code which wishes to report an exception can catch these two types, and use
30 * the 'what()' method to report the text of the exception message.
31 *
32 * Sadly, there is no documentation (I'm aware of) or specification of the character set/code page reported
33 * back by the what () on an exception. It tends to be ascii. Stroika guarantees that all exceptions it throws
34 * will use the current SDK characters (@see SDKString). But - its best to use Characters::ToString () on the
35 * caught exception, since this uses ExceptionStringHelper to properly handle characters the SDK character set might not
36 * allow representing some unicode characters.
37 *
38 * TODO:
39 * @todo COULD MAYBE add 'suppress-activities' object you can declare and while it exists (it sets private thread local storage flag)
40 * activities are not merged into message. But this can be done other ways too and doesn't address other stages of message
41 * formation so I'm not sure that's worth while.
42 */
43
45
46 /**
47 * The type of 'errno' variable.
48 *
49 * \note POSIX and C99 just say to assume its an int and doesn't define errno_t.
50 * But I find this type usage clearer.
51 */
52#if qCompilerAndStdLib_Supports_errno_t
53 using errno_t = ::errno_t;
54#else
55 using errno_t = int;
56#endif
57
58 /**
59 * This is a base class for Execution::Exception<> template, which gets mixed with the std c++ exception class, to mix
60 * in Stroika string support.
61 *
62 * This probably should NOT be used directly.
63 */
65 public:
66 /**
67 * \brief activities default to NULLOPT - meaning "not specified" - which is different from "specified,
68 * and there were none".
69 *
70 * Nothing is captured at construction. @see Throw () fills them in at the point of throw, but only for
71 * an exception which has not specified them - so an exception built once and thrown many times (e.g. a
72 * `static const`) reports the context of each throw rather than freezing the first one.
73 *
74 * Pass them explicitly here, or @see ImbueActivities (), if you are not going through Throw ().
75 */
79
80 public:
81 /**
82 * Return error message without added 'activities'.
83 */
84 nonvirtual Characters::String GetBasicErrorMessage () const;
85
86 public:
87 /**
88 * Return error message with added 'activities'
89 */
90 nonvirtual Characters::String GetFullErrorMessage () const;
91
92 public:
93 /**
94 * Return the activity stack from when the exception was thrown. NOTE - see @Activity<>. This has little
95 * todo with the thread runtime stack. It refers to a logical stack of declared Activity<> objects.
96 */
98
99 public:
100 /**
101 * \brief Set, replace, or clear the activity stack. Any already-computed message text is
102 * discarded, since it depends on these.
103 *
104 * Takes an optional for symmetry with the constructor and @see GetActivities (), so that every
105 * state is reachable. Passing nullopt returns the exception to "not specified", after which
106 * @see Throw () will stamp in the context of the throw - which is what you want when forwarding
107 * an exception you did not construct.
108 *
109 * \note ***This invalidates any pointer previously returned by what ().*** That is allowed, and is
110 * why this is deliberately a NON-const member: the standard guarantees what ()'s result only
111 * "until the exception object is destroyed, or until a non-const member function on the
112 * exception object is called". Do not hold a what () pointer across a call to this.
113 *
114 * @see Throw () - which imbues a COPY it is about to throw, so nobody can be holding a
115 * what () pointer into it - is the normal caller, and is unaffected.
116 *
117 * \note Replacing is allowed on purpose: a caller doing this explicitly knows what it wants.
118 * @see Throw () is the one that will not overwrite - it imbues only when GetActivities () is
119 * nullopt, so deliberately-specified context always survives being thrown.
120 */
122
123 public:
124 /**
125 * Only implemented for
126 * o wstring
127 * o String
128 *
129 * This returns the message callers should display to represent the error (e.g. in exception::c_str ()).
130 * For now, it returns GetFullErrorMessage () - but may someday change.
131 */
132 template <typename T>
133 nonvirtual T As () const;
134
135 protected:
136 /**
137 * In order for subclasses to support the c++ exception::c_str () API, we need to convert
138 * the string/message to an SDKChar string, and have that lifetime be very long. So we store it
139 * in a std::string. And this function returns the pointer to that string. This object is
140 * immutable, so that the lifetime of the underlying return const char* is as long as this object.
141 */
142 nonvirtual const char* _PeekAtNarrowSDKString () const;
143
144 private:
145 /**
146 * Builds fFullErrorMessage_/fSDKCharString_ if not already built. Cheap and safe to call repeatedly,
147 * and safe to call concurrently (an exception object can be shared via exception_ptr).
148 *
149 * \note These CANNOT be computed at construction: activities may be supplied afterwards by
150 * @see ImbueActivities (), and the full message merges them in. Computing eagerly would mean
151 * either baking in the wrong context or recomputing.
152 *
153 * \note noexcept because its callers are - notably what (). If formatting fails (allocation), the
154 * cache is left empty rather than propagating.
155 */
156 nonvirtual void EnsureBuilt_ () const noexcept;
157
158 private:
159 optional<Containers::Stack<Activity<>>> fActivities_;
160 Characters::String fRawErrorMessage_;
161 mutable mutex fBuildMutex_;
162 mutable bool fBuilt_{false};
163 mutable Characters::String fFullErrorMessage_;
164 mutable string fSDKCharString_;
165 };
166 template <>
167 wstring ExceptionStringHelper::As () const;
168 template <>
170
171 /**
172 * \brief Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::exception'),
173 * which adds UNICODE String support.
174 *
175 * Stroika's Exception<> class is fully interoperable with the normal C++ exception classes, but its use offers two
176 * benefits:
177 * o It guarantees that UNICODE messages (including things like filenames) are properly preserved in the exception
178 * message, even if the system default code page (locale) does not allow representing those characters.
179 *
180 * o It integrates neatly with the Stroika 'Activity' mechanism, whereby you declare current 'activities' on the stack
181 * and these are automatically integrated into exceptions thrown with Execution::Throw (), to provide clearer messages (e.g. instead of
182 * getting the error message
183 * o "(errno: 13)" OR
184 * o "Permission denied"
185 * Stroika returns something like:
186 * o "Permission denied {errno: 13} while binding to INADDR_ANY:80, while constructing static content webserver."
187 *
188 * @see Activity<>
189 * @see DeclareActivity
190 *
191 * \par Example Usage
192 * \code
193 * static constexpr Activity kBuildingThingy_ {"building thingy"sv };
194 * try {
195 * DeclareActivity declareActivity { &kBuildingThingy_ };
196 * doBuildThing (); // Execution::Throw () any Exception<>
197 * }
198 * catch (...) {
199 * String exceptionMsg = Characters::ToString (current_exception ());
200 * Assert (exceptionMsg.Contains (kBuildingThingy_.AsString ())); // exception e while building thingy...
201 * }
202 * \endcode
203 *
204 * \note Satisfies:
205 * o Exception<std::exception>{msg} - default-constructible
206 * o Exception<std::bad_alloc>{msg} - default-constructible
207 * o Exception<std::runtime_error>{msg} - requires a what_arg
208 * o Exception<std::logic_error>{msg} - requires a what_arg
209 * o Exception<std::out_of_range>{msg} - requires a what_arg
210 *
211 * \note Deliberately NOT std::system_error: it needs an error_code, not a string, so it satisfies
212 * neither constraint and must use the protected delegating ctor below - @see
213 * SystemErrorException. (No static_assert for this one, because it is not portable: libstdc++
214 * declares `system_error (error_code = error_code ())`, so THERE it is default-constructible
215 * and this ctor does match, yielding a useless empty code. MSVC has no such default. Do not
216 * rely on either behaviour.)
217 */
218 template <derived_from<exception> BASE_EXCEPTION = exception>
219 class Exception : public ExceptionStringHelper, public BASE_EXCEPTION {
220 private:
221 using inherited = BASE_EXCEPTION;
222
223 public:
224 /**
225 * \brief construct with just a message - BASE_EXCEPTION gets whatever it minimally needs.
226 *
227 * Two overloads with mutually exclusive constraints, because the std exception types split into two
228 * groups and NEITHER spelling covers both: some are default-constructible, and some instead require
229 * a what_arg (and have no default ctor). The latter get "" - they need *some* string and do not care
230 * which, since @see what () is overridden to return the Stroika message regardless.
231 */
232 Exception () = delete;
233 Exception (const Exception&) = default;
238
239 protected:
240 /**
241 * For BASE_EXCEPTION classes with constructors OTHER than just 'message' - you cannot use a 'using X = Exception(x)' but a subclass
242 * which uses this delegating method.
243 */
244 template <typename... BASE_EXCEPTION_ARGS>
246
247 public:
248 /**
249 * Provide a 'c string' variant of the exception message. Convert the UNICODE
250 * string argument to a narrow-string (multibyte) in the current locale encoding.
251 */
252 virtual const char* what () const noexcept override;
253 };
254
255#if qCompilerAndStdLib_CTADIgnoresConstructorRequiresClause_Buggy
256 /**
257 * \brief explicit deduction guide so 'Exception{msg}' (no explicit template argument) deduces
258 * Exception<exception>, rather than being ambiguous.
259 *
260 * Without this, CTAD on 'Exception{msg}' has to choose between the two implicitly-generated guides
261 * for the two constructors above, which differ only in their requires-clause (mutually exclusive, but
262 * only once BASE_EXCEPTION is known - a chicken-and-egg problem for a purely argument-driven deduction).
263 * This explicit guide is a non-template exact match, so ordinary overload resolution picks it over
264 * either implicit (function-template) guide outright, without needing to evaluate their constraints.
265 *
266 * @see qCompilerAndStdLib_CTADIgnoresConstructorRequiresClause_Buggy
267 */
269#endif
270
271 static_assert (constructible_from<Exception<exception>, Characters::String>); // see Satisfies
276
277 /**
278 * \brief NestedException contains a new higher level error message (typically based on argument basedOnException)
279 * and preserves the original exception (which you can use to get its message, with Characters::ToString (fBasedOnException)
280 *
281 * This also can be used to wrap third-party libraries exceptions, which don't know about 'DeclareActivity' - and incorporating
282 * that into an error message.
283 */
284 class NestedException : public Execution::Exception<runtime_error> {
285 public:
286 /**
287 */
288 NestedException () = delete;
289 NestedException (const NestedException&) = default;
290 NestedException (const exception_ptr& basedOnException);
291 NestedException (const Characters::String& msg, const exception_ptr& basedOnException);
292
293 public:
294 const exception_ptr fBasedOnException;
295 };
296
297 /**
298 * Simple wrapper on std::system_error, but adding support for Stroika String, and other utility methods.
299 *
300 * \note see https://en.cppreference.com/w/cpp/error/errc for a mapping of errc conditions and ERRNO values.
301 *
302 * \note **Catch `system_error`, not `SystemErrorException`.** Catching the base costs you nothing -
303 * Characters::ToString () still recovers the UNICODE message and the Activity stack (@see Exception<>) -
304 * and it additionally covers what this type does NOT: errors from non-Stroika code (which throws plain
305 * system_error), and Stroika errors built over OTHER system_error subclasses, such as
306 * IO::FileSystem::Exception (an @see Exception<filesystem_error>).
307 *
308 * \note The reverse applies when THROWING: prefer SystemErrorException over a plain std::system_error.
309 * It preserves the UNICODE message, and carries the Activity stack that @see Throw () stamps in
310 * (@see Exception<>); a plain system_error can hold neither, and the catcher cannot recover what
311 * was never there. And since SystemErrorException IS a system_error, throwing the
312 * richer type costs nothing. In short: catch broadly (system_error), throw richly
313 * (SystemErrorException, or let @see ThrowError () build it for you).
314 *
315 * \note ***Changed in Stroika v3.0d25*** - this is no longer a template; spell it without the `<>`.
316 * It exists only to add error_code-shaped constructors to Exception<system_error>, and those
317 * constructors are meaningful ONLY for system_error - no other standard system_error subclass has
318 * a constructor taking a bare error_code (filesystem_error, for one, requires a what_arg). The old
319 * `SystemErrorException<BASE_EXCEPTION>` therefore offered a generality it could not deliver.
320 * To mix Stroika's string/Activity support into a DIFFERENT system_error subclass, derive from
321 * @see Exception<THAT_TYPE> directly - which is what IO::FileSystem::Exception now does.
322 *
323 * \par Example Usage
324 * \code
325 * try {
326 * ThrowPOSIXErrNo (make_error_code (errc::bad_address).value ());
327 * }
328 * catch (const std::system_error& e) {
329 * EXPECT_TRUE (e.code ().value () == make_error_code (errc::bad_address).value ());
330 * EXPECT_TRUE (e.code ().category () == system_category () or e.code ().category () == generic_category ());
331 * Assert (Characters::ToString (e).Contains ("bad address {errno: 14}"));
332 * }
333 * \endcode
334 *
335 * Note this preserves UNICODE characters in messages, even if not using UNICODE code page/locale
336 * \par Example Usage
337 * \code
338 * try {
339 * const Characters::String kMsgWithUnicode_ = L"zß水𝄋"; // this works even if using a code page / locale which doesn't support UNICODE/Chinese
340 * try {
341 * Execution::Throw (SystemErrorException (make_error_code (errc::bad_address), kMsgWithUnicode_));
342 * }
343 * catch (const std::system_error& e) {
344 * Assert (Characters::ToString (e).Contains (kMsgWithUnicode_)); // message also includes the number for bad_address
345 * }
346 * \endcode
347 *
348 * \par Example Usage
349 * \code
350 * try {
351 * s.JoinMulticastGroup (UPnP::SSDP::V4::kSocketAddress.GetInternetAddress ());
352 * }
353 * catch (const std::system_error& e) {
354 * if (Execution::IsA (e, errc::no_such_device)) {
355 * // This can happen on Linux when you start before you have a network connection - no problem - just keep trying
356 * DbgTrace ("Got exception (errno: ENODEV) - while joining multicast group, so try again");
357 * Execution::Sleep (1);
358 * goto Again;
359 * }
360 * else {
361 * Execution::ReThrow ();
362 * }
363 * }
364 * \endcode
365 *
366 * \note The constructors which take no message build one from the error_code, via its category. For a
367 * few codes whose platform wording is poor or actively misleading, Stroika substitutes a clearer
368 * string instead - notably errc::timed_out, which glibc renders "Connection timed out" even for a
369 * mutex or event wait. @see ThrowError for the full rule and rationale.
370 *
371 * \note ***Considered and declined 2026-09-22: removing this type altogether.*** The argument was that it
372 * is a footgun - someone can catch it, and thereby silently miss every error Stroika raises over a
373 * DIFFERENT system_error subclass - and that anything it does could be had from Exception<system_error>
374 * directly.
375 *
376 * The first half is true, which is why the advice above exists. The second is not: these constructors
377 * synthesize the message from the error_code (@see Private_::SystemErrorExceptionPrivate_), including
378 * the substitutions described below, and that machinery is private. Removing the class would push
379 * message synthesis onto every throw site, or replace it with a factory function that is the same
380 * thing without a name - neither simpler.
381 *
382 * In practice the exposure is small: @see ThrowError () is the normal path and builds this for you,
383 * and direct construction is rare. So the type stays, and the rule stays: throw richly, catch broadly.
384 *
385 * @see also GetAssociatedErrorCode ()
386 */
387 class SystemErrorException : public Exception<system_error> {
388 private:
390
391 public:
392 /**
393 */
394 SystemErrorException (error_code errCode);
395 SystemErrorException (error_code errCode, const Characters::String& message);
396 SystemErrorException (int ev, const error_category& ecat);
397 SystemErrorException (int ev, const error_category& ecat, const Characters::String& message);
398 };
399
400 /**
401 * \brief treats argument errNo as a `POSIX errno` value, and calls ThrowError () with an error_code constructed from it
402 *
403 * \pre errNo != 0
404 *
405 * \note Since this calls @see ThrowError - it will throw a @see SystemErrorException (or a derived type) or bad_alloc.
406 *
407 * \note On a POSIX system, this amounts to a call to ThrowSystemErrNo.
408 * But even on a non-POSIX system, many APIs map their error numbers to POSIX error numbers so this can make sense to use.
409 * Also, on POSIX systems, its legal to call this with POSIX compatible extended (and therefore not POSIX) erorr nubmbers.
410 * In other words, you can call this with anything (except 0) you read out of errno on a POSIX system.
411 *
412 * \note From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf -
413 * "If the argument ev corresponds to a POSIX errno value posv, the function
414 * shall return error_- condition(posv, generic_category()). Otherwise, the function"
415 *
416 * \note this function takes errno as a default value because you almost always want to call it with the value from errno.
417 *
418 * See:
419 * @see ThrowSystemErrNo ();
420 */
422
423 /**
424 * Look at the argument value and if < 0,ThrowPOSIXErrNo (), and otherwise return it.
425 *
426 * \note Many POSIX - APIs - return a number which is zero if good, or -1 (or sometimes defined negative) if errno is set and there is an error.
427 * This function is useful for wrapping calls to those style functions. It checks if the argument result is negative (so -1 covers that) and
428 * throws and a POSIX (generic_error) SystemErrorException.
429 */
430 template <typename INT_TYPE>
432
433 /**
434 * \brief treats argument sysErr as a platform-defined error number, and calls ThrowError () with an error_code constructed from it.
435 *
436 * \pre sysErr != 0
437 *
438 * \note Since this calls @see ThrowError - it will throw a @see SystemErrorException (or a derived type) or bad_alloc.
439 *
440 * \note stdc++ uses 'int' for the type of this error number, but Windows generally defines the type to be
441 * DWORD.
442 *
443 * \par Which error_category it uses
444 * o POSIX: std::system_category ()
445 * o Windows: Platform::Windows::Win32_error_category () - the same value and name () ("system")
446 * as std::system_category (), but it maps more codes onto the portable errc conditions
447 * (@see Platform::Windows::Win32_error_category)
448 *
449 * So test what you catch with IsA (e, errc::...), or on Windows Platform::Windows::IsWin32Error
450 * (e.code (), ERROR_X) - never by comparing with error_code{n, system_category ()}.
451 *
452 * \note This follows the rule the standard library sets for its own errors ([value.error.codes],
453 * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf): "That object's category()
454 * member shall return std::system_category() for errors originating from the operating system,
455 * or a reference to an implementation-defined error_category object for errors originating
456 * elsewhere." Windows is the deliberate exception - why: https://github.com/SophistSolutions/Stroika/issues/1192
457 *
458 * \par Example Usage
459 * \code
460 * #if qStroika_Platform_POSIX
461 * ThrowSystemErrNo (errno);
462 * #elif qStroika_Platform_Windows
463 * ThrowSystemErrNo (::GetLastError ()); // works with this type of error # - GetLastError () is default if no arg provided
464 * ThrowSystemErrNo (::WSAGetLastError ()); // or this
465 * #endif
466 * \endcode
467 *
468 * See:
469 * @see ThrowPOSIXErrNo ();
470 *
471 * \note zero arg versions only defined for POSIX and Windows platforms, and there the default is the obvious value for each
472 * platform - errno and GetLastError(). It is still an assertion (require) error to call these when errno / GetLastError () would return 0.
473 */
474 [[noreturn]] void ThrowSystemErrNo (int sysErr);
475#if qStroika_Platform_POSIX or qStroika_Platform_Windows
476 [[noreturn]] void ThrowSystemErrNo (); ///< \brief ThrowSystemErrNo (errno) on POSIX, ThrowSystemErrNo (::GetLastError ()) on Windows
477#endif
478
479 /**
480 * \brief throw a SystemErrorException with the given error_code - or std::bad_alloc for errc::not_enough_memory, the one type promotion documented below.
481 *
482 * \par This throws EITHER a SystemErrorException (a subclass of system_error), OR std::bad_alloc.
483 *
484 * Use this - rather than constructing and throwing a SystemErrorException directly - whenever you have an
485 * error_code from ANY error_category: the OS, or a third party library which defines its own category
486 * (libcurl, getaddrinfo/DNS, HRESULT, ...). It is what gives callers one portable story about how errors
487 * surface, independent of where they came from.
488 *
489 * \pre ec != error_code{}
490 *
491 * \par What a caller must catch
492 * This throws EITHER something derived from std::system_error, OR std::bad_alloc. There is no third
493 * possibility. So a caller which wants to handle everything needs:
494 *
495 * \code
496 * try {
497 * ...
498 * }
499 * catch (const system_error& e) {
500 * // everything except memory exhaustion; always test the CONDITION, not the value
501 * if (Execution::IsA (e, errc::timed_out)) { ... }
502 * DbgTrace ("{}"_f, Characters::ToString (e)); // NB: ToString (e), NOT e.what () - see below
503 * }
504 * catch (const bad_alloc&) {
505 * // memory exhaustion ONLY - see below for why this is separate
506 * }
507 * \endcode
508 *
509 * \note **Catch std::system_error, NOT SystemErrorException** - even though today ThrowError () only
510 * ever throws the latter. Catching the base additionally covers errors from non-Stroika code (which
511 * throws plain system_error), and Stroika errors built over other system_error subclasses -
512 * IO::FileSystem::Exception is an Exception<filesystem_error>, and so is NOT a SystemErrorException.
513 * @see the note on SystemErrorException itself, which gives the same advice.
514 *
515 * (For reference, the C++ standard defines exactly two subclasses of system_error:
516 * std::ios_base::failure and std::filesystem::filesystem_error. std::future_error is NOT one of
517 * them - it derives from logic_error - and neither are C++20's chrono local-time exceptions.)
518 *
519 * \note **Catching the base costs you nothing - as long as you read the message with
520 * Characters::ToString (e) rather than e.what ().** ToString () does a dynamic_cast to
521 * Execution::ExceptionStringHelper (@see Characters::ToStringDefaults::ToString), and a
522 * dynamic_cast cross-casts, so it finds the Stroika half through ANY base reference -
523 * system_error, or even std::exception. What it returns is As<String> (), which is
524 * GetFullErrorMessage (), so you get the full UNICODE text **with the Activity stack already
525 * merged in** - exactly what you would have got by catching the derived type.
526 *
527 * e.what () is the lossy path: it is narrow SDK-charset, so characters the SDK code page cannot
528 * represent do not survive. That is the whole reason Exception<> exists (@see Exception<>).
529 *
530 * If you want the activities structurally rather than as message text, dynamic_cast to
531 * Execution::ExceptionStringHelper yourself and call GetActivities ().
532 *
533 * The bad_alloc case is the ONLY reason two handlers are needed, and it is deliberate: bad_alloc does
534 * not derive from system_error, because allocation failure is the one error C++ has always expected you
535 * to catch by its own standard type. Folding it in as just another error_code would be more uniform but
536 * would fight decades of convention, so it is called out separately instead.
537 *
538 * \par The one type promotion
539 * The rule is deliberately narrow: **promote only to a type the C++ standard itself already defines for
540 * that condition.** That yields exactly one entry:
541 *
542 * | condition | thrown type | still a SystemErrorException? |
543 * |---------------------------|------------------|-------------------------------|
544 * | `errc::not_enough_memory` | `std::bad_alloc` | **no** |
545 *
546 * Every other error_code - errc::timed_out included - throws a plain SystemErrorException: test a
547 * timeout with `Execution::IsA (e, errc::timed_out)`.
548 *
549 * \par What is preserved, and what is not
550 * **ThrowError () preserves what the error MEANS. It does not promise to preserve how that meaning is
551 * REPRESENTED.** Where the two conflict it normalizes - sometimes lossily - so the error can be tested
552 * portably. In order of how much is given up:
553 *
554 * o Usually nothing: the error_code is passed through untouched, and only the thrown type is chosen
555 * from it. `e.code ().category ()` still names where the error came from, which is what makes
556 * library-specific diagnosis possible.
557 * o Windows codes keep their raw value: the conditions MSVC's system_category leaves out (a WinHTTP
558 * timeout, a refused connection, ...) come from @see Platform::Windows::Win32_error_category, which
559 * ThrowSystemErrNo () tags them with - so nothing needs rewriting (as WAIT_TIMEOUT and
560 * ERROR_INTERNET_TIMEOUT once were).
561 * o `errc::not_enough_memory` becomes `std::bad_alloc`, giving up the error_code entirely - bad_alloc
562 * has no code () - because that is the type C++ has always expected you to catch for allocation
563 * failure. This is also why it needs its own catch handler, and why GetAssociatedErrorCode ()
564 * returns nullopt for it. It gives up the caller's message and the Activity stack too; that is
565 * deliberate, and the note below says why.
566 *
567 * \note **Why the bad_alloc case alone is allowed to be this lossy.** Everywhere else, Stroika works hard
568 * to carry a UNICODE message and the Activity context (@see Exception<>). Here it deliberately
569 * does not, and the reason is that the enrichment would need the very resource that just ran out:
570 * building a message and copying an activity stack means several allocations, performed while
571 * reporting that an allocation failed. Under genuine memory pressure that can throw from inside
572 * the throw, turning a clean bad_alloc into something far harder to diagnose. std::bad_alloc is
573 * specified to be cheap to create for exactly this reason, and wrapping it would throw that away.
574 *
575 * Little is lost in practice. @see Throw () already traces the message and the activities at the
576 * throw point, which is where anyone diagnosing this would look; and an out-of-memory failure is
577 * rarely recoverable, so rich structured context buys less here than anywhere else.
578 *
579 * Considered and rejected 2026-09-09: making this throw an Exception<bad_alloc> so the message and
580 * activities survive. It works, and `catch (const bad_alloc&)` would keep matching - but it trades
581 * a reliable failure for a richer one that is least reliable exactly when it matters. Revisit only
582 * with a concrete case where the lost context actually mattered.
583 *
584 * So: **test what the error means** - `e.code () == errc::whatever`, or `catch (const bad_alloc&)` -
585 * and none of this normalization is visible to you. Test how it is represented -
586 * `e.code ().value () == SOME_PLATFORM_CONSTANT` - and it is.
587 * @see ThrowTranslatedExceptionIfNeeded_ in Exceptions.cpp for the mechanism.
588 *
589 * **If you do not want any of that** - if you want the exception object you constructed delivered to
590 * the catcher unchanged - then say so by using @see Throw () instead. That is the whole distinction
591 * between the two: ThrowError () normalizes, possibly lossily, in exchange for portability; Throw ()
592 * is literal.
593 *
594 * The narrow rule is what stops the table being arbitrary. The obvious question about any such table is
595 * "why this condition and not permission_denied, or connection_refused?" - and the answer is that C++
596 * defines std::bad_alloc and defines no counterpart for the others. Nothing is invented.
597 *
598 * \par How Stroika treats errc::timed_out, and what its default message says
599 * **Stroika treats errc::timed_out as the generic "the time limit for this operation expired"
600 * condition** - nothing connection-specific. Every Stroika timeout answers to it: a Synchronized<>
601 * lock acquisition, Thread::WaitForDone (), a WaitableEvent, a socket read, a libcurl transfer. That
602 * uniformity is what makes `Execution::IsA (e, errc::timed_out)` the one portable timeout test.
603 *
604 * That is the condition's actual meaning, not a Stroika invention. POSIX specifies ETIMEDOUT - which
605 * std::errc::timed_out names - as the result of pthread_mutex_timedlock (), pthread_cond_timedwait (),
606 * sem_timedwait () and mq_timedreceive (), none of which involve a connection.
607 *
608 * \note ***Changed in Stroika v3.0d25*** - the DEFAULT message now reflects that. glibc renders
609 * ETIMEDOUT as "Connection timed out" (a BSD-sockets legacy - ETIMEDOUT began life as a
610 * connect () error), while MSVC renders it "timed out": so before v3.0d25 the very same lock
611 * timeout was described differently on each platform, and misleadingly on one. Stroika now
612 * substitutes **"Operation timed out"** when it builds a default message for errc::timed_out in
613 * `generic_category ()` - which is exactly the case where STROIKA decided something timed out,
614 * since make_error_code () always yields that category. A timeout reported by the OS arrives in
615 * `system_category ()` (@see ThrowPOSIXErrNo) and keeps the platform's own wording, which is
616 * accurate there. A message you pass explicitly is never touched.
617 *
618 * Nothing portable is being overridden: the standard specifies error_category::message () only
619 * as "a string that describes the error condition", so the text is implementation-defined and
620 * the two platforms already disagreed. @see SystemErrorException, whose constructor is where a
621 * default message actually gets built.
622 *
623 * \par Relationship to ThrowPOSIXErrNo () and ThrowSystemErrNo ()
624 * All three funnel through here and behave identically once they do. They differ ONLY in which
625 * error_category the raw number gets tagged with - that is, in how the error_code is built:
626 *
627 * | function | category used |
628 * |------------------------|-----------------------------------------------------------|
629 * | `ThrowError (ec)` | whatever category you put in the error_code |
630 * | `ThrowSystemErrNo (n)` | `system_category ()` (on Windows `Platform::Windows::Win32_error_category ()`) |
631 * | `ThrowPOSIXErrNo (n)` | `system_category ()` on POSIX, else `generic_category ()` |
632 *
633 * So on a POSIX system ThrowPOSIXErrNo and ThrowSystemErrNo are the same call - errno IS the
634 * platform's native error space there, so one subsumes the other. They diverge only on Windows,
635 * where errno and GetLastError () are distinct numbering spaces and must not be tagged with the
636 * same category. Pick whichever names where your number actually came from.
637 *
638 * **The category is not just a label.** It supplies default_error_condition () and equivalent (), which
639 * is what every `e.code () == errc::something` test - and the bad_alloc promotion above - is matched
640 * against. So tagging a number with the wrong category does not merely mislabel the error, it silently
641 * changes which conditions the error appears to satisfy. WAIT_TIMEOUT in a Win32 category compares
642 * equal to errc::timed_out; the same number tagged with generic_category () is interpreted as an errno
643 * instead, and matches something else or nothing at all. That - not tidiness - is why these two
644 * functions must stay separate on Windows. (And it is why Win32 codes get Stroika's
645 * Win32_error_category () rather than plain system_category (), whose mapping misses, for example,
646 * ERROR_WINHTTP_TIMEOUT.)
647 *
648 * \note Matching is driven by the CONDITION, not the raw value, so any category which maps its own
649 * numbering onto std::errc (as the LibCurl and DNS categories both do) participates automatically.
650 * That is precisely what error_category::default_error_condition is for - a category author gets
651 * this simply by writing a correct mapping.
652 *
653 * \par Example Usage
654 * \code
655 * // wrapping a third party library which has its own error_category
656 * if (status != kThirdPartyOK) [[unlikely]] {
657 * ThrowError (error_code{status, ThirdParty_error_category ()});
658 * }
659 * \endcode
660 *
661 * \par Raising a portable condition directly - the std::errc overload
662 * When the error did not come from anywhere with its own numbering - Stroika itself has decided the
663 * operation timed out, say - there is no category to name, and `ThrowError (errc::timed_out)` says so
664 * directly:
665 *
666 * \code
667 * ThrowError (errc::timed_out, "Timeout Expired"sv); // instead of
668 * ThrowError (make_error_code (errc::timed_out), "Timeout Expired"sv);
669 * \endcode
670 *
671 * This is exactly `ThrowError (make_error_code (ec))` and nothing else. It is unambiguous with the
672 * error_code overloads because `is_error_code_enum_v<errc>` is **false** - std::errc has no implicit
673 * conversion to error_code - so overload resolution cannot pick the wrong one.
674 *
675 * \note Use the error_code overload whenever you HAVE a category. std::errc names a *condition*, and
676 * make_error_code () resolves it to `generic_category ()` - it asserts the error originated as a
677 * POSIX errno. That is true when Stroika raises the error itself; it is NOT true of an error handed
678 * back by the OS or a third-party library, and flattening one of those to an errc discards where it
679 * came from. Since `e.code () == errc::whatever` matches either way, nothing is gained by
680 * discarding the category.
681 *
682 * See:
683 * @see ThrowPOSIXErrNo ();
684 * @see ThrowSystemErrNo ();
685 */
686 [[noreturn]] void ThrowError (error_code ec);
687 [[noreturn]] void ThrowError (error_code ec, const Characters::String& message);
688 [[noreturn]] void ThrowError (errc ec);
689 [[noreturn]] void ThrowError (errc ec, const Characters::String& message);
690
691 /**
692 * \brief Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from the set of errors that can be returned
693 *
694 * Run the given (argument) call. After each call, invoke Thread::CheckForInterruption ().
695 * If the call returns < 0 and errno == EINTR, repeat the call.
696 * If the result was < 0, but errno != EINTR, then ThrowErrNoIfNegative ();
697 * Then return the result.
698 *
699 * \note The only HITCH with respect to automatically handling interruptability is that that its handled by 'restarting' the argument 'call'
700 * That means if it was partially completed, the provider of 'call' must accommodate that fact (use mutable lambda).
701 *
702 * This behavior is meant to work with the frequent POSIX API semantics of a return value of < 0
703 * implying an error, and < 0 but errno == EINTR means retry the call. This API also provides a
704 * cancelation point - so it makes otherwise blocking calls (like select, or read) work well with thread
705 * interruption.
706 *
707 * \note ***Cancelation Point***
708 */
709 template <typename CALL>
710 auto Handle_ErrNoResultInterruption (CALL call) -> decltype (call ());
711
712 /**
713 * Check the argument 'return value' from some function, and if its null, throw a SystemError exception with
714 * the current errno value.
715 *
716 * \note rarely useful, but some POSIX APIs such as getcwd() do return null on error.
717 */
719
720 /**
721 * This checks if the given exception_ptr is of a type that contains an error code, and if so
722 * it extracts the error code, and returns it (else nullopt).
723 *
724 * \par Example Usage
725 * \code
726 * try {
727 * /// do something that throws
728 * }
729 * catch (...) {
730 * if (auto err = GetAssociatedErrorCode (current_exception ())) {
731 * if (*err == errc::no_such_device) {
732 * // This can happen on Linux when you start before you have a network connection - no problem - just keep trying
733 * DbgTrace ("Got exception (errno: ENODEV) - while joining multicast group, so try again");
734 * Execution::Sleep (1);
735 * goto Again;
736 * }
737 * else {
738 * Execution::ReThrow ();
739 * }
740 * }
741 * }
742 * \endcode
743 */
745
746 /**
747 * \brief Does this error MEAN the given condition - regardless of how it happens to be represented?
748 *
749 * This is the recommended way to test an error in Stroika code. It is `e.code () == cond` with the
750 * question asked once, correctly, and with overloads for the things you actually hold - including
751 * exception_ptr, which the standard gives you no way to interrogate without rethrowing.
752 *
753 * \note basically same as e.code () == cond, but safer (less error prone and not MATCHES on differnt error_category, etc)
754 *
755 * \par Example Usage
756 * \code
757 * try {
758 * t.WaitForDone (1s);
759 * }
760 * catch (const system_error& e) {
761 * if (Execution::IsA (e, errc::timed_out)) {
762 * ...
763 * }
764 * }
765 * \endcode
766 *
767 * \note ***Why this exists, and why it takes an error_condition***
768 *
769 * std::error_code and std::error_condition look interchangeable and are not. A CODE is a
770 * concrete value in some category - `errno`, a Win32 error, a libcurl status. A CONDITION is
771 * the portable meaning those map onto. Comparing a code to a condition asks what the error
772 * MEANS and works whatever category it arrived in. Comparing `e.code ().value ()` to a number
773 * asks how it is REPRESENTED, and is right only if you guessed the category correctly - it
774 * compiles, it looks reasonable, and it is usually wrong.
775 *
776 * That trap is inherited from the c++ standard and Stroika cannot remove it. What it can do is
777 * make the recommended form incapable of expressing it: the second parameter is an
778 * error_condition BY VALUE, so `IsA (e, errc::timed_out)` converts implicitly and compiles,
779 * while `IsA (e, ERROR_FILE_NOT_FOUND)` and `IsA (e, someErrorCode)` do not compile at all.
780 *
781 * `e.code () == errc::X` remains perfectly correct, and existing code using it is fine.
782 *
783 * \note On Windows, IsA () also answers for a Win32 code that arrived in std::system_category () (from the
784 * standard library, say) as if it were in Platform::Windows::Win32_error_category () - so it gets the
785 * conditions Microsoft's mapping leaves out. A raw `e.code () == errc::X` does not get that for such a
786 * code; IsA () is the one portable test.
787 *
788 * \note The exception_ptr and exception overloads answer false for anything that carries no error
789 * code, which includes a null exception_ptr. They never throw.
790 *
791 * @see GetAssociatedErrorCode
792 * @see Tests/37, Test5_error_code_condition_compares_ - which demonstrates the trap directly.
793 */
794 bool IsA (const error_code& ec, error_condition cond) noexcept;
795 bool IsA (const system_error& e, error_condition cond) noexcept; ///< \brief Does this error MEAN the given condition?
796 bool IsA (const exception& e, error_condition cond) noexcept; ///< \brief ...and false if it carries no error code at all
797 bool IsA (const exception_ptr& e, error_condition cond) noexcept; ///< \brief ...without having to rethrow it to find out
798
799 /**
800 * Wrap the the argument function (typically a lambda) in an OPTIONAL of the argument type, and return nullopt - dropping the exception
801 * on the floor.
802 *
803 * @todo When we support std c++23, do likewise for expected!!!
804 */
805 template <typename F>
807
808}
809
810#if qStroika_Platform_Windows
812
813 /**
814 * \brief The error_category Stroika tags Win32 error numbers (GetLastError ()) with: std::system_category (), plus the
815 * portable conditions Microsoft's leaves out.
816 *
817 * MSVC's system_category maps a Win32 code onto a portable condition with one flat, one-to-one table, which knows
818 * nothing of the WinINet / WinHTTP range (so ERROR_WINHTTP_TIMEOUT is not errc::timed_out) and misses winerror.h's
819 * own connection errors (ERROR_CONNECTION_REFUSED, ...) although it maps their Winsock twins. Its message () also
820 * says "unknown error" for codes whose text lives in a module (wininet.dll / winhttp.dll). This category answers
821 * those, and delegates everything else - name () included - to std::system_category ().
822 *
823 * Mapping a numbering onto conditions is what an error_category is FOR, so this keeps the raw code (unlike
824 * rewriting it at throw time), and both `e.code () == errc::X` and IsA () work. It maps a code onto MORE than one
825 * condition where the meaning is ambiguous (via equivalent ()), since Win32 codes do not divide as neatly as
826 * POSIX errno does. @see https://github.com/SophistSolutions/Stroika/issues/1192 for the alternatives considered.
827 *
828 * \note A Win32 error_code in std::system_category () - from the standard library or another library - is
829 * still a different code from the same number in this category. Execution::IsA () treats it as this
830 * category, so test with IsA (), or with IsWin32Error () for an exact code - not `ec == error_code{...}`.
831 *
832 * This object lives forever (like other error categories).
833 */
834 const error_category& Win32_error_category () noexcept;
835
836 /**
837 * \brief Is ec the given Win32 error number - in Win32_error_category () or std::system_category ()?
838 *
839 * Use this instead of `ec == error_code{ERROR_X, system_category ()}`, which no longer matches errors Stroika throws
840 * (@see Win32_error_category).
841 */
842 bool IsWin32Error (const error_code& ec, int win32Err) noexcept;
843
844}
845#endif
846
847/*
848 ********************************************************************************
849 ***************************** Implementation Details ***************************
850 ********************************************************************************
851 */
852#include "Exceptions.inl"
853
854#endif /*_Stroika_Foundation_Execution_Exceptions_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:219
Exception()=delete
construct with just a message - BASE_EXCEPTION gets whatever it minimally needs.
virtual const char * what() const noexcept override
nonvirtual void ImbueActivities(const optional< Containers::Stack< Activity<> > > &activities)
Set, replace, or clear the activity stack. Any already-computed message text is discarded,...
nonvirtual optional< Containers::Stack< Activity<> > > GetActivities() const
nonvirtual Characters::String GetBasicErrorMessage() const
nonvirtual Characters::String GetFullErrorMessage() const
ExceptionStringHelper()=delete
activities default to NULLOPT - meaning "not specified" - which is different from "specified,...
nonvirtual const char * _PeekAtNarrowSDKString() const
NestedException contains a new higher level error message (typically based on argument basedOnExcepti...
Definition Exceptions.h:284
void ThrowPOSIXErrNo(errno_t errNo=errno)
treats argument errNo as a POSIX errno value, and calls ThrowError () with an error_code constructed ...
auto Handle_ErrNoResultInterruption(CALL call) -> decltype(call())
Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from th...
void ThrowPOSIXErrNoIfNull(void *returnValue)
auto TranslateExceptionToOptional(F &&f) -> optional< remove_cvref_t< invoke_result_t< F > > >
optional< error_code > GetAssociatedErrorCode(const exception_ptr &e) noexcept
void ThrowError(error_code ec)
throw a SystemErrorException with the given error_code - or std::bad_alloc for errc::not_enough_memor...
INT_TYPE ThrowPOSIXErrNoIfNegative(INT_TYPE returnCode)
bool IsA(const error_code &ec, error_condition cond) noexcept
Does this error MEAN the given condition - regardless of how it happens to be represented?