]>
Commit | Line | Data |
---|---|---|
92f5a8d4 | 1 | /* Proposed SG14 status_code |
f67539c2 | 2 | (C) 2018-2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits) |
92f5a8d4 TL |
3 | File Created: Feb 2018 |
4 | ||
5 | ||
6 | Boost Software License - Version 1.0 - August 17th, 2003 | |
7 | ||
8 | Permission is hereby granted, free of charge, to any person or organization | |
9 | obtaining a copy of the software and accompanying documentation covered by | |
10 | this license (the "Software") to use, reproduce, display, distribute, | |
11 | execute, and transmit the Software, and to prepare derivative works of the | |
12 | Software, and to permit third-parties to whom the Software is furnished to | |
13 | do so, all subject to the following: | |
14 | ||
15 | The copyright notices in the Software and this entire statement, including | |
16 | the above license grant, this restriction and the following disclaimer, | |
17 | must be included in all copies of the Software, in whole or in part, and | |
18 | all derivative works of the Software, unless such copies or derivative | |
19 | works are solely in the form of machine-executable object code generated by | |
20 | a source language processor. | |
21 | ||
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
24 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT | |
25 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE | |
26 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, | |
27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
28 | DEALINGS IN THE SOFTWARE. | |
29 | */ | |
30 | ||
31 | #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_WIN32_CODE_HPP | |
32 | #define BOOST_OUTCOME_SYSTEM_ERROR2_WIN32_CODE_HPP | |
33 | ||
34 | #if !defined(_WIN32) && !defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) | |
35 | #error This file should only be included on Windows | |
36 | #endif | |
37 | ||
38 | #include "generic_code.hpp" | |
39 | ||
40 | BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN | |
41 | ||
42 | //! \exclude | |
43 | namespace win32 | |
44 | { | |
45 | // A Win32 DWORD | |
46 | using DWORD = unsigned long; | |
47 | // Used to retrieve the current Win32 error code | |
48 | extern DWORD __stdcall GetLastError(); | |
49 | // Used to retrieve a locale-specific message string for some error code | |
50 | extern DWORD __stdcall FormatMessageW(DWORD dwFlags, const void *lpSource, DWORD dwMessageId, DWORD dwLanguageId, wchar_t *lpBuffer, DWORD nSize, void /*va_list*/ *Arguments); | |
51 | // Converts UTF-16 message string to UTF-8 | |
52 | extern int __stdcall WideCharToMultiByte(unsigned int CodePage, DWORD dwFlags, const wchar_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, int cbMultiByte, const char *lpDefaultChar, int *lpUsedDefaultChar); | |
53 | #pragma comment(lib, "kernel32.lib") | |
f67539c2 TL |
54 | #if defined(_WIN64) |
55 | #pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YAKXZ=GetLastError") | |
92f5a8d4 TL |
56 | #pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YAKKPEBXKKPEA_WKPEAX@Z=FormatMessageW") |
57 | #pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YAHIKPEB_WHPEADHPEBDPEAH@Z=WideCharToMultiByte") | |
f67539c2 TL |
58 | #else |
59 | #pragma comment(linker, "/alternatename:?GetLastError@win32@system_error2@@YGKXZ=__imp__GetLastError@0") | |
60 | #pragma comment(linker, "/alternatename:?FormatMessageW@win32@system_error2@@YGKKPBXKKPA_WKPAX@Z=__imp__FormatMessageW@28") | |
61 | #pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@system_error2@@YGHIKPB_WHPADHPBDPAH@Z=__imp__WideCharToMultiByte@32") | |
62 | #endif | |
92f5a8d4 TL |
63 | } // namespace win32 |
64 | ||
65 | class _win32_code_domain; | |
66 | class _com_code_domain; | |
67 | //! (Windows only) A Win32 error code, those returned by `GetLastError()`. | |
68 | using win32_code = status_code<_win32_code_domain>; | |
69 | //! (Windows only) A specialisation of `status_error` for the Win32 error code domain. | |
70 | using win32_error = status_error<_win32_code_domain>; | |
71 | ||
72 | /*! (Windows only) The implementation of the domain for Win32 error codes, those returned by `GetLastError()`. | |
f67539c2 | 73 | */ |
92f5a8d4 TL |
74 | class _win32_code_domain : public status_code_domain |
75 | { | |
76 | template <class DomainType> friend class status_code; | |
77 | template <class StatusCode> friend class detail::indirecting_domain; | |
78 | friend class _com_code_domain; | |
79 | using _base = status_code_domain; | |
80 | static int _win32_code_to_errno(win32::DWORD c) | |
81 | { | |
82 | switch(c) | |
83 | { | |
84 | case 0: | |
85 | return 0; | |
86 | #include "detail/win32_code_to_generic_code.ipp" | |
87 | } | |
88 | return -1; | |
89 | } | |
90 | //! Construct from a Win32 error code | |
91 | static _base::string_ref _make_string_ref(win32::DWORD c) noexcept | |
92 | { | |
93 | wchar_t buffer[32768]; | |
94 | win32::DWORD wlen = win32::FormatMessageW(0x00001000 /*FORMAT_MESSAGE_FROM_SYSTEM*/ | 0x00000200 /*FORMAT_MESSAGE_IGNORE_INSERTS*/, nullptr, c, 0, buffer, 32768, nullptr); | |
95 | size_t allocation = wlen + (wlen >> 1); | |
96 | win32::DWORD bytes; | |
97 | if(wlen == 0) | |
98 | { | |
99 | return _base::string_ref("failed to get message from system"); | |
100 | } | |
101 | for(;;) | |
102 | { | |
103 | auto *p = static_cast<char *>(malloc(allocation)); // NOLINT | |
104 | if(p == nullptr) | |
105 | { | |
106 | return _base::string_ref("failed to get message from system"); | |
107 | } | |
108 | bytes = win32::WideCharToMultiByte(65001 /*CP_UTF8*/, 0, buffer, (int) (wlen + 1), p, (int) allocation, nullptr, nullptr); | |
109 | if(bytes != 0) | |
110 | { | |
111 | char *end = strchr(p, 0); | |
112 | while(end[-1] == 10 || end[-1] == 13) | |
113 | { | |
114 | --end; | |
115 | } | |
116 | *end = 0; // NOLINT | |
117 | return _base::atomic_refcounted_string_ref(p, end - p); | |
118 | } | |
119 | free(p); // NOLINT | |
120 | if(win32::GetLastError() == 0x7a /*ERROR_INSUFFICIENT_BUFFER*/) | |
121 | { | |
122 | allocation += allocation >> 2; | |
123 | continue; | |
124 | } | |
125 | return _base::string_ref("failed to get message from system"); | |
126 | } | |
127 | } | |
128 | ||
129 | public: | |
130 | //! The value type of the win32 code, which is a `win32::DWORD` | |
131 | using value_type = win32::DWORD; | |
132 | using _base::string_ref; | |
133 | ||
134 | public: | |
135 | //! Default constructor | |
f67539c2 TL |
136 | constexpr explicit _win32_code_domain(typename _base::unique_id_type id = 0x8cd18ee72d680f1b) noexcept |
137 | : _base(id) | |
138 | { | |
139 | } | |
92f5a8d4 TL |
140 | _win32_code_domain(const _win32_code_domain &) = default; |
141 | _win32_code_domain(_win32_code_domain &&) = default; | |
142 | _win32_code_domain &operator=(const _win32_code_domain &) = default; | |
143 | _win32_code_domain &operator=(_win32_code_domain &&) = default; | |
144 | ~_win32_code_domain() = default; | |
145 | ||
146 | //! Constexpr singleton getter. Returns the constexpr win32_code_domain variable. | |
147 | static inline constexpr const _win32_code_domain &get(); | |
148 | ||
149 | virtual string_ref name() const noexcept override { return string_ref("win32 domain"); } // NOLINT | |
150 | protected: | |
151 | virtual bool _do_failure(const status_code<void> &code) const noexcept override // NOLINT | |
152 | { | |
153 | assert(code.domain() == *this); | |
154 | return static_cast<const win32_code &>(code).value() != 0; // NOLINT | |
155 | } | |
156 | virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override // NOLINT | |
157 | { | |
158 | assert(code1.domain() == *this); | |
159 | const auto &c1 = static_cast<const win32_code &>(code1); // NOLINT | |
160 | if(code2.domain() == *this) | |
161 | { | |
162 | const auto &c2 = static_cast<const win32_code &>(code2); // NOLINT | |
163 | return c1.value() == c2.value(); | |
164 | } | |
165 | if(code2.domain() == generic_code_domain) | |
166 | { | |
167 | const auto &c2 = static_cast<const generic_code &>(code2); // NOLINT | |
168 | if(static_cast<int>(c2.value()) == _win32_code_to_errno(c1.value())) | |
169 | { | |
170 | return true; | |
171 | } | |
172 | } | |
173 | return false; | |
174 | } | |
175 | virtual generic_code _generic_code(const status_code<void> &code) const noexcept override // NOLINT | |
176 | { | |
177 | assert(code.domain() == *this); | |
178 | const auto &c = static_cast<const win32_code &>(code); // NOLINT | |
179 | return generic_code(static_cast<errc>(_win32_code_to_errno(c.value()))); | |
180 | } | |
181 | virtual string_ref _do_message(const status_code<void> &code) const noexcept override // NOLINT | |
182 | { | |
183 | assert(code.domain() == *this); | |
184 | const auto &c = static_cast<const win32_code &>(code); // NOLINT | |
185 | return _make_string_ref(c.value()); | |
186 | } | |
187 | #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) | |
188 | BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override // NOLINT | |
189 | { | |
190 | assert(code.domain() == *this); | |
191 | const auto &c = static_cast<const win32_code &>(code); // NOLINT | |
192 | throw status_error<_win32_code_domain>(c); | |
193 | } | |
194 | #endif | |
195 | }; | |
196 | //! (Windows only) A constexpr source variable for the win32 code domain, which is that of `GetLastError()` (Windows). Returned by `_win32_code_domain::get()`. | |
197 | constexpr _win32_code_domain win32_code_domain; | |
198 | inline constexpr const _win32_code_domain &_win32_code_domain::get() | |
199 | { | |
200 | return win32_code_domain; | |
201 | } | |
202 | ||
203 | BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END | |
204 | ||
205 | #endif |