Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Throw.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_Throw_h_
5#define _Stroika_Foundation_Execution_Throw_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <exception>
10#include <memory>
11#include <stdexcept>
12#include <system_error>
13
14#include "Stroika/Foundation/Common/Common.h"
15#include "Stroika/Foundation/Common/Concepts.h"
17
18/**
19 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
20 *
21 * \note \em Design Note
22 * (essentially) All exceptions thrown by Stroika (except where needed by quirks of underlying library
23 * integrated with) inherit from std::exception, or Stroika::Foundation::Execution::SilentException.
24 *
25 * This means that any code which wishes to report an exception can catch these two types, and use
26 * the 'what()' method to report the text of the exception message.
27 *
28 * Sadly, there is no documentation (I'm aware of) or specification of the character set/code page reported
29 * back by the what () on an exception. It tends to be ascii. Stroika guarantees that all exceptions it throws
30 * will use the current SDK characters (@see SDKString). But - its best to check for inheriting from
31 * Exception<>, since the SDK character set might not allow representing some unicode characters.
32 */
33
35
36 /**
37 * qStroika_Foundation_Execution_Throw_TraceThrowpoint controls whether or not Stroika will DbgTrace () on
38 * (essentially) all exception throws.
39 *
40 * This is nearly always desirable, but in applications that do lots of exceptions (probably not a good idea), this can produce
41 * a lot of trace log noise, and some people object to it.
42 *
43 * Since this only affects calls to DbgTrace () - it only has effect if qStroika_Foundation_Debug_DefaultTracingOn is on.
44 *
45 * \note - to turn this on, you can add the flag
46 * --c-define '\#define qStroika_Foundation_Execution_Throw_TraceThrowpoint 0'
47 * to your configure line
48 */
49#ifndef qStroika_Foundation_Execution_Throw_TraceThrowpoint
50#define qStroika_Foundation_Execution_Throw_TraceThrowpoint qStroika_Foundation_Debug_DefaultTracingOn
51#endif
52
53 /**
54 * qStroika_Foundation_Execution_Throw_TraceThrowpointBacktrace is only meant for debugging. If true, then the
55 * overloads to Throw will also include a call to BackTrace, so its easier to track in TraceLogs where an exception
56 * is thrown from (helpful when you don't have a debugger).
57 *
58 * This has no effect unless qStroika_Foundation_Execution_Throw_TraceThrowpoint is also true.
59 */
60#ifndef qStroika_Foundation_Execution_Throw_TraceThrowpointBacktrace
61#define qStroika_Foundation_Execution_Throw_TraceThrowpointBacktrace qStroika_Foundation_Debug_DefaultTracingOn
62#endif
63
64 /**
65 * \brief like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,
66 * type dependent DbgTrace () messages first
67 *
68 * Utility to call a Trace message (hopefully an appropriate one) for an exception being thrown.
69 *
70 * \note **Throw () and ThrowError () are a deliberate pair, and choosing between them is a real
71 * decision - not a matter of which argument type you happen to have:**
72 *
73 * o `ThrowError (ec)` - *"report this error in Stroika's NORMALIZED form."* It may change
74 * the type thrown, and may give up part of how the error was represented, so that what
75 * arrives can be tested portably. @see ThrowError for exactly what is preserved (the
76 * meaning) and what can be given up (the representation).
77 *
78 * o `Throw (x)` - *"throw exactly THIS."* No translation, no promotion, nothing lossy: the
79 * catcher gets the object you constructed, with its TYPE and its MESSAGE unchanged. That
80 * is a deliberate property, not a shortcoming: Throw (X) throwing something other than X
81 * would be a far worse surprise. (It does fill in the Activity context when the exception
82 * did not specify one - see below - which adds to what the exception reports without
83 * changing what it is.)
84 *
85 * The consequence is that constructing a SystemErrorException yourself and throwing it here skips
86 * the promotion documented at @see Execution::ThrowError - so a code whose condition is
87 * errc::not_enough_memory arrives as a SystemErrorException rather than std::bad_alloc, and a
88 * caller who wrote catch (const bad_alloc&) does not see it.
89 *
90 * Doing that is perfectly legal, and occasionally what you want. But it is almost never what you
91 * want when the error_code came from the OS or from a library with its own error_category
92 * (libcurl's CURLE_OUT_OF_MEMORY, say): there, you want ThrowError (ec).
93 * **If you have an error_code, reach for ThrowError () first, and use Throw () only if you have a
94 * reason to want the promotion suppressed.**
95 *
96 * \note ***Changed in Stroika v3.0d25*** Throw () stamps the CURRENT Activity stack into the exception -
97 * but only when the exception did not specify one (GetActivities () == nullopt). It imbues a COPY,
98 * so the object you passed is not modified. That is what lets a `static const` exception - built
99 * once and thrown many times - report each throw's own context instead of freezing whichever
100 * context happened to be live the first time. Activities specified deliberately (passed to the
101 * constructor, or set with ImbueActivities ()) are never overwritten, and an exception thrown with
102 * a bare `throw X{}` gets no Activity context at all.
103 * @see ExceptionStringHelper::ImbueActivities.
104 *
105 * \note What you hand Throw () also decides what the CATCHER gets. Stroika's Exception<> types - and so
106 * SystemErrorException and everything derived from it - preserve UNICODE message text, and can
107 * carry the Activity context described above (@see Exception<>). Throw (system_error{ec})
108 * carries neither, and neither can be recovered later by whoever catches it. Since
109 * SystemErrorException IS a std::system_error, throwing the Stroika type costs nothing in
110 * interoperability - so prefer Throw (SystemErrorException{ec}), or better still ThrowError (ec).
111 *
112 * \note The overloads which take an extra trace message are JUST for convenience, and vector through the
113 * 1-arg overload.
114 */
115 template <typename T>
116 [[noreturn]] void Throw (T&& e2Throw);
117 template <typename T>
118 [[noreturn]] void Throw (T&& e2Throw, const char* traceMsg);
119 template <typename T>
120 [[noreturn]] void Throw (T&& e2Throw, const wchar_t* traceMsg);
121
122 /**
123 * Just a regular C++ rethrow, but with a DbgTrace message...
124 */
125 [[noreturn]] void ReThrow ();
126 [[noreturn]] void ReThrow (const exception_ptr& e);
127 [[noreturn]] void ReThrow (const char* traceMsg);
128 [[noreturn]] void ReThrow (const exception_ptr& e, const char* traceMsg);
129 [[noreturn]] void ReThrow (const wchar_t* traceMsg);
130 [[noreturn]] void ReThrow (const exception_ptr& e, const wchar_t* traceMsg);
131
132 /**
133 * @brief Throws an exception if the argument is null (nullptr, or nullopt, or similar). Optionally takes specific exception to throw (defaults to bad_alloc).
134 *
135 * If the first argument is null, throw the second argument exception (which defaults to bad_alloc)
136 */
137 template <equality_comparable_with<nullptr_t> T, typename E>
138 void ThrowIfNull (const T& p, const E& e);
139 template <equality_comparable_with<nullptr_t> T>
140 void ThrowIfNull (const T& p);
141 template <Common::Weak_Equality_Comparable_With<nullopt_t> T, typename E>
142 void ThrowIfNull (const T& p, const E& e)
143 requires (not equality_comparable_with<nullptr_t, T>);
144 template <Common::Weak_Equality_Comparable_With<nullopt_t> T>
145 void ThrowIfNull (const T& p)
146 requires (not equality_comparable_with<nullptr_t, T>);
147
148 /**
149 * If the first argument is failed throw the exception, and else return the value. Works with std::expected, to Common::StdCompat::expected
150 *
151 * For some reason, this doesn't appear to be part of the std::expected interface, so we define it here. Maybe there is a way with
152 * transform, etc, but it doesn't appear to be so (without using a two step process where you create the expected, and then can
153 * reference it in a second step, binding it into the lambda with transform_error or some such).
154 */
155 template <typename EXPECTED>
156 typename EXPECTED::value_type ThrowIfFailed (const EXPECTED& e);
157
158 /**
159 * \def IgnoreExceptionsForCall - ignore all exceptions for the given argument call (evaluate arg)
160 *
161 * @see IgnoreExceptionsExceptThreadAbortForCall
162 */
163#define IgnoreExceptionsForCall(theCode) \
164 try { \
165 theCode; \
166 } \
167 catch (...) { \
168 }
169
170 /**
171 * \def IgnoreExceptionsExceptThreadAbortForCall - ignore all exceptions (except thread abort) for the given argument call (evaluate arg)
172 *
173 * @see IgnoreExceptionsForCall
174 */
175#define IgnoreExceptionsExceptThreadAbortForCall(theCode) \
176 try { \
177 theCode; \
178 } \
179 catch (const Stroika::Foundation::Execution::Thread::AbortException&) { \
180 Execution::ReThrow (); \
181 } \
182 catch (...) { \
183 }
184
185}
186
187/*
188 ********************************************************************************
189 ***************************** Implementation Details ***************************
190 ********************************************************************************
191 */
192#include "Throw.inl"
193
194#endif /*_Stroika_Foundation_Execution_Throw_h_*/
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...
EXPECTED::value_type ThrowIfFailed(const EXPECTED &e)
Definition Throw.inl:190