Stroika Library 3.0d20
 
Loading...
Searching...
No Matches
Exceptions.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <cstdio>
7
8#if qStroika_Foundation_Common_Platform_Windows
9#include <Windows.h>
10#include <wininet.h> // for error codes
11#endif
12
15#include "Stroika/Foundation/Linguistics/MessageUtilities.h"
16
17#include "Throw.h"
18#include "TimeOutException.h"
19
20#include "Exceptions.h"
21
22using namespace Stroika::Foundation;
23using namespace Characters;
24using namespace Execution;
25
27
28// Comment this in to turn on aggressive noisy DbgTrace in this module
29//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
30
31namespace {
32 // @todo this message needs lots of linguistic cleanup (punctuation, capitalization etc) - started but quite incomplete
33 String mkMessage_ (const Characters::String& reasonForError, const Containers::Stack<Activity<>>& activities)
34 {
35 //
36 // @todo rewrite this using Linguistics::CurrentLocaleMessageUtilities so we capture the 'while' crap there too
37 //
38 if (activities.empty ()) {
39 return reasonForError;
40 }
42 auto tmp = Linguistics::MessageUtilities::Manager::sThe.RemoveTrailingSentencePunctuation (reasonForError);
43 sb << tmp.first;
44 sb << " while "sv;
45 for (auto i = activities.begin (); i != activities.end ();) {
46 sb << i->AsString ();
47 ++i;
48 if (i == activities.end ()) {
49 sb << tmp.second.value_or ("."sv);
50 }
51 else {
52 // not clear yet what message will work here
53 sb << ", while "sv;
54 }
55 }
56 return sb;
57 }
58}
59
60/*
61 ********************************************************************************
62 ****************************** ExceptionStringHelper ***************************
63 ********************************************************************************
64 */
66 : fActivities_{activities}
67 , fRawErrorMessage_{reasonForError}
68 , fFullErrorMessage_{mkMessage_ (reasonForError, activities)}
69 , fSDKCharString_{fFullErrorMessage_.AsNarrowSDKString (eIgnoreErrors)}
70{
71}
72
73/*
74 ********************************************************************************
75 ********************************* NestedException ******************************
76 ********************************************************************************
77 */
78NestedException::NestedException (const exception_ptr& basedOnException)
79 : NestedException{Characters::ToString (basedOnException), basedOnException}
80{
81}
82
83/*
84 ********************************************************************************
85 ***************** Private_::SystemErrorExceptionPrivate_ ***********************
86 ********************************************************************************
87 */
88#if qStroika_Foundation_Common_Platform_Windows
89
90// for InternetGetConnectedState
91#if _MSC_VER
92#pragma comment(lib, "Wininet.lib")
93#endif
94
95optional<String> TryToOverrideDefaultWindowsSystemCategoryMessage_ (error_code errCode)
96{
97 if (errCode.category () == system_category ()) {
98 switch (errCode.value ()) {
99 case ERROR_NOT_ENOUGH_MEMORY:
100 return "Not enough memory to complete that operation (ERROR_NOT_ENOUGH_MEMORY)"sv;
101 case ERROR_OUTOFMEMORY:
102 return "Not enough memory to complete that operation (ERROR_OUTOFMEMORY)"sv;
103 case WSAEADDRNOTAVAIL:
104 return "Socket address not available (WSAEADDRNOTAVAIL)"sv;
105 case ERROR_INTERNET_INVALID_URL:
106 return "ERROR_INTERNET_INVALID_URL"sv;
107 case ERROR_INTERNET_CANNOT_CONNECT:
108 return "Failed to connect to internet URL (ERROR_INTERNET_CANNOT_CONNECT)"sv;
109 case ERROR_INTERNET_NAME_NOT_RESOLVED:
110 return "ERROR_INTERNET_NAME_NOT_RESOLVED"sv;
111 case ERROR_INTERNET_INCORRECT_HANDLE_STATE:
112 return "ERROR_INTERNET_INCORRECT_HANDLE_STATE"sv;
113 case ERROR_INTERNET_TIMEOUT:
114 return "Operation timed out (ERROR_INTERNET_TIMEOUT)"sv;
115 case ERROR_INTERNET_CONNECTION_ABORTED:
116 return "ERROR_INTERNET_CONNECTION_ABORTED"sv;
117 case ERROR_INTERNET_CONNECTION_RESET:
118 return "ERROR_INTERNET_CONNECTION_RESET"sv;
119 case ERROR_HTTP_INVALID_SERVER_RESPONSE:
120 return "Invalid Server Response (ERROR_HTTP_INVALID_SERVER_RESPONSE)"sv;
121 case ERROR_INTERNET_PROTOCOL_NOT_FOUND: {
122 DWORD r = 0;
123 if (::InternetGetConnectedState (&r, 0) and (r & INTERNET_CONNECTION_OFFLINE) == 0) {
124 return "ERROR_INTERNET_PROTOCOL_NOT_FOUND"sv;
125 }
126 else {
127 return "ERROR_INTERNET_PROTOCOL_NOT_FOUND (offline mode)"sv;
128 }
129 }
130 }
131 }
132 return nullopt;
133}
134#endif
135Characters::String Execution::Private_::SystemErrorExceptionPrivate_::mkMsg_ (error_code errCode)
136{
137#if qStroika_Foundation_Common_Platform_Windows
138 // for some messages, the default windows implementation does poorly generating messages
139 if (optional<String> o = TryToOverrideDefaultWindowsSystemCategoryMessage_ (errCode)) {
140 return *o;
141 }
142#endif
143 // Let the standard C++ library generate the default error message for the given error code - from the category object
144 return Characters::String::FromNarrowSDKString (errCode.message ());
145}
146
147Characters::String Execution::Private_::SystemErrorExceptionPrivate_::mkCombinedMsg_ (error_code errCode, const Characters::String& message)
148{
149 StringBuilder sb{message};
150 sb += " ";
151 if (errCode.category () == generic_category ()) {
152 sb += "{{errno: {}}}"_f(errCode.value ());
153 }
154 else if (errCode.category () == system_category ()) {
155#if qStroika_Foundation_Common_Platform_POSIX
156 sb += "{{errno: {}}}"_f(errCode.value ());
157#elif qStroika_Foundation_Common_Platform_Windows
158 sb += "{{Windows error: {}}}"_f(errCode.value ());
159#else
160 sb += "{{system error: {}}}"_f(errCode.value ());
161#endif
162 }
163 else {
164 sb += "{{{}: {}}}"_f(Characters::String::FromNarrowSDKString (errCode.category ().name ()), errCode.value ());
165 }
166 return sb;
167}
168
169void Execution::Private_::SystemErrorExceptionPrivate_::TranslateException_ (error_code errCode)
170{
171 // SEE - SystemErrorExceptionPrivate_::TranslateExceptionQuietly_
172 if (errCode == errc::not_enough_memory) {
173 Throw (bad_alloc{});
174 }
175 if (errCode == errc::timed_out) {
176 Throw (TimeOutException{errCode});
177 }
178#if qCompilerAndStdLib_Winerror_map_doesnt_map_timeout_Buggy
179 if (errCode.category () == system_category ()) {
180 switch (errCode.value ()) {
181 case WAIT_TIMEOUT: // errc::timed_out
182 case ERROR_INTERNET_TIMEOUT: // ""
183 // NOT a good idea because then code saying if (errCode==errc::timed_out) will still fail --- Throw (TimeOutException (errCode));
184 Throw (TimeOutException::kThe); // sad to have to lose the original error, but kind of useful so if test against errc::timeout works
185 }
186 }
187#endif
188
189 // double check the compare-with-conditions code working the way I think its supposed to... matching multiple error codes -- LGP 2019-02-04
190#if qStroika_Foundation_Common_Platform_Windows && qStroika_Foundation_Debug_AssertionsChecked
191 if (errCode.category () == system_category ()) {
192 switch (errCode.value ()) {
193 case ERROR_NOT_ENOUGH_MEMORY: // errc::not_enough_memory
194 case ERROR_OUTOFMEMORY: // ""
195 case WAIT_TIMEOUT: // errc::timed_out
196 case ERROR_INTERNET_TIMEOUT: // ""
197 AssertNotReached (); // should have been caught above in if (ec == errc::... checks) - so thats not working - maybe need to add this switch or debug
198 // qCompilerAndStdLib_Winerror_map_doesnt_map_timeout_Buggy???
199 break;
200 }
201 }
202#endif
203}
204
205/*
206 ********************************************************************************
207 ***** SystemErrorExceptionPrivate_::TranslateExceptionQuietly_ *****************
208 ********************************************************************************
209 */
210unique_ptr<exception> Execution::Private_::SystemErrorExceptionPrivate_::TranslateExceptionQuietly_ (error_code errCode)
211{
212 // MIMIC - SystemErrorExceptionPrivate_::TranslateException_
213 if (errCode == errc::not_enough_memory) {
214 return make_unique<bad_alloc> ();
215 }
216 if (errCode == errc::timed_out) {
217 return make_unique<TimeOutException> (errCode);
218 }
219#if qCompilerAndStdLib_Winerror_map_doesnt_map_timeout_Buggy
220 if (errCode.category () == system_category ()) {
221 switch (errCode.value ()) {
222 case WAIT_TIMEOUT:
223 case ERROR_INTERNET_TIMEOUT:
224 return make_unique<TimeOutException> ();
225 }
226 }
227#endif
228 return nullptr;
229}
230
231/*
232 ********************************************************************************
233 *********************** Execution::GetAssociatedErrorCode **********************
234 ********************************************************************************
235 */
236optional<error_code> Execution::GetAssociatedErrorCode (const exception_ptr& e) noexcept
237{
238 try {
239 rethrow_exception (e);
240 }
241 catch (const system_error& se) {
242 return se.code ();
243 }
244 catch (...) {
245 return nullopt;
246 }
247}
#define AssertNotReached()
Definition Assertions.h:355
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
static String FromNarrowSDKString(const char *from)
Definition String.inl:470
NestedException contains a new higher level error message (typically based on argument basedOnExcepti...
Definition Exceptions.h:212
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:465
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43