1 /* Proposed SG14 status_code
2 (C) 2018 - 2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
3 File Created: Feb 2018
4 
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License in the accompanying file
9 Licence.txt or at
10 
11 http://www.apache.org/licenses/LICENSE-2.0
12 
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 
19 
20 Distributed under the Boost Software License, Version 1.0.
21 (See accompanying file Licence.txt or copy at
22 http://www.boost.org/LICENSE_1_0.txt)
23 */
24 
25 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_CONFIG_HPP
26 #define BOOST_OUTCOME_SYSTEM_ERROR2_CONFIG_HPP
27 
28 // < 0.1 each
29 #include <cassert>
30 #include <cstddef>  // for size_t
31 #include <cstdlib>  // for free
32 
33 // 0.22
34 #include <type_traits>
35 
36 // 0.29
37 #include <atomic>
38 
39 // 0.28 (0.15 of which is exception_ptr)
40 #include <exception>  // for std::exception
41 // <new> includes <exception>, <exception> includes <new>
42 #include <new>
43 
44 // 0.01
45 #include <initializer_list>
46 
47 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14
48 #if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */
49 //! Defined to be `constexpr` when on C++ 14 or better compilers. Usually automatic, can be overriden.
50 #define BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 constexpr
51 #else
52 #define BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14
53 #endif
54 #endif
55 
56 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
57 #if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || (_HAS_CXX17 && _MSC_VER >= 1911 /* VS2017.3 */)
58 #define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN [[noreturn]]
59 #endif
60 #endif
61 #if !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN)
62 #ifdef __has_cpp_attribute
63 #if __has_cpp_attribute(noreturn)
64 #define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN [[noreturn]]
65 #endif
66 #endif
67 #endif
68 #if !defined(BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN)
69 #if defined(_MSC_VER)
70 #define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN __declspec(noreturn)
71 #elif defined(__GNUC__)
72 #define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN __attribute__((__noreturn__))
73 #else
74 #define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
75 #endif
76 #endif
77 // GCCs before 7 don't grok [[noreturn]] virtual functions, and warn annoyingly
78 #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 7
79 #undef BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
80 #define BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN
81 #endif
82 
83 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
84 #if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || (_HAS_CXX17 && _MSC_VER >= 1911 /* VS2017.3 */)
85 #define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD [[nodiscard]]
86 #endif
87 #endif
88 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
89 #ifdef __has_cpp_attribute
90 #if __has_cpp_attribute(nodiscard)
91 #define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD [[nodiscard]]
92 #endif
93 #elif defined(__clang__)
94 #define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD __attribute__((warn_unused_result))
95 #elif defined(_MSC_VER)
96 // _Must_inspect_result_ expands into this
97 #define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD                                                                                                                                                                                                                                                                                                \
98   __declspec("SAL_name"                                                                                                                                                                                                                                                                                                        \
99              "("                                                                                                                                                                                                                                                                                                               \
100              "\"_Must_inspect_result_\""                                                                                                                                                                                                                                                                                       \
101              ","                                                                                                                                                                                                                                                                                                               \
102              "\"\""                                                                                                                                                                                                                                                                                                            \
103              ","                                                                                                                                                                                                                                                                                                               \
104              "\"2\""                                                                                                                                                                                                                                                                                                           \
105              ")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") __declspec("SAL_checkReturn") __declspec("SAL_end")
106 #endif
107 #endif
108 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
109 #define BOOST_OUTCOME_SYSTEM_ERROR2_NODISCARD
110 #endif
111 
112 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI
113 #if defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE) || (__clang_major__ >= 7 && !defined(__APPLE__))
114 //! Defined to be `[[clang::trivial_abi]]` when on a new enough clang compiler. Usually automatic, can be overriden.
115 #define BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI [[clang::trivial_abi]]
116 #else
117 #define BOOST_OUTCOME_SYSTEM_ERROR2_TRIVIAL_ABI
118 #endif
119 #endif
120 
121 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE
122 //! The system_error2 namespace name.
123 #define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE system_error2
124 //! Begins the system_error2 namespace.
125 #define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN                                                                                                                                                                                                                                                                                          \
126   namespace system_error2                                                                                                                                                                                                                                                                                                      \
127   {
128 //! Ends the system_error2 namespace.
129 #define BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END }
130 #endif
131 
132 //! Namespace for the library
133 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
134 
135 //! Namespace for user specialised traits
136 namespace traits
137 {
138   /*! Specialise to true if you guarantee that a type is move bitcopying (i.e.
139   its move constructor equals copying bits from old to new, old is left in a
140   default constructed state, and calling the destructor on a default constructed
141   instance is trivial). All trivially copyable types are move bitcopying by
142   definition, and that is the unspecialised implementation.
143   */
144   template <class T> struct is_move_bitcopying
145   {
146     static constexpr bool value = std::is_trivially_copyable<T>::value;
147   };
148 }  // namespace traits
149 
150 namespace detail
151 {
152 #if __cplusplus >= 201400 || _MSC_VER >= 1910 /* VS2017 */
cstrlen(const char * str)153   inline constexpr size_t cstrlen(const char *str)
154   {
155     const char *end = nullptr;
156     for(end = str; *end != 0; ++end)  // NOLINT
157       ;
158     return end - str;
159   }
160 #else
161   inline constexpr size_t cstrlen_(const char *str, size_t acc)
162   {
163     return (str[0] == 0) ? acc : cstrlen_(str + 1, acc + 1);
164   }
165   inline constexpr size_t cstrlen(const char *str)
166   {
167     return cstrlen_(str, 0);
168   }
169 #endif
170 
171   /* A partially compliant implementation of C++20's std::bit_cast function contributed
172   by Jesse Towner. TODO FIXME Replace with C++ 20 bit_cast when available.
173 
174   Our bit_cast is only guaranteed to be constexpr when both the input and output
175   arguments are either integrals or enums. However, this covers most use cases
176   since the vast majority of status_codes have an underlying type that is either
177   an integral or enum. We still attempt a constexpr union-based type pun for non-array
178   input types, which some compilers accept. For array inputs, we fall back to
179   non-constexpr memmove.
180   */
181 
182   template <class T> using is_integral_or_enum = std::integral_constant<bool, std::is_integral<T>::value || std::is_enum<T>::value>;
183 
184   template <class To, class From> using is_static_castable = std::integral_constant<bool, is_integral_or_enum<To>::value && is_integral_or_enum<From>::value>;
185 
186   template <class To, class From> using is_union_castable = std::integral_constant<bool, !is_static_castable<To, From>::value && !std::is_array<To>::value && !std::is_array<From>::value>;
187 
188   template <class To, class From> using is_bit_castable = std::integral_constant<bool, sizeof(To) == sizeof(From) && traits::is_move_bitcopying<To>::value && traits::is_move_bitcopying<From>::value>;
189 
190   template <class To, class From> union bit_cast_union {
191     From source;
192     To target;
193   };
194 
195   template <class To, class From,
196             typename std::enable_if<                 //
197             is_bit_castable<To, From>::value         //
198             && is_static_castable<To, From>::value   //
199             && !is_union_castable<To, From>::value,  //
200             bool>::type = true>                      //
bit_cast(const From & from)201   constexpr To bit_cast(const From &from) noexcept
202   {
203     return static_cast<To>(from);
204   }
205 
206 #if defined(__GNUC__) && !defined(__clang__)
207 #pragma GCC diagnostic push
208 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
209 #endif
210   template <class To, class From,
211             typename std::enable_if<                 //
212             is_bit_castable<To, From>::value         //
213             && !is_static_castable<To, From>::value  //
214             && is_union_castable<To, From>::value,   //
215             bool>::type = true>                      //
bit_cast(const From & from)216   constexpr To bit_cast(const From &from) noexcept
217   {
218     return bit_cast_union<To, From>{from}.target;
219   }
220 #if defined(__GNUC__) && !defined(__clang__)
221 #pragma GCC diagnostic pop
222 #endif
223 
224   template <class To, class From,
225             typename std::enable_if<                 //
226             is_bit_castable<To, From>::value         //
227             && !is_static_castable<To, From>::value  //
228             && !is_union_castable<To, From>::value,  //
229             bool>::type = true>                      //
230   To bit_cast(const From &from) noexcept
231   {
232     bit_cast_union<To, From> ret;
233     memmove(&ret.source, &from, sizeof(ret.source));
234     return ret.target;
235   }
236 
237   /* erasure_cast performs a bit_cast with additional rules to handle types
238   of differing sizes. For integral & enum types, it may perform a narrowing
239   or widing conversion with static_cast if necessary, before doing the final
240   conversion with bit_cast. When casting to or from non-integral, non-enum
241   types it may insert the value into another object with extra padding bytes
242   to satisfy bit_cast's preconditions that both types have the same size. */
243 
244   template <class To, class From> using is_erasure_castable = std::integral_constant<bool, traits::is_move_bitcopying<To>::value && traits::is_move_bitcopying<From>::value>;
245 
246   template <class T, bool = std::is_enum<T>::value> struct identity_or_underlying_type
247   {
248     using type = T;
249   };
250   template <class T> struct identity_or_underlying_type<T, true>
251   {
252     using type = typename std::underlying_type<T>::type;
253   };
254 
255   template <class OfSize, class OfSign>
256   using erasure_integer_type = typename std::conditional<std::is_signed<typename identity_or_underlying_type<OfSign>::type>::value, typename std::make_signed<typename identity_or_underlying_type<OfSize>::type>::type, typename std::make_unsigned<typename identity_or_underlying_type<OfSize>::type>::type>::type;
257 
258   template <class ErasedType, std::size_t N> struct padded_erasure_object
259   {
260     static_assert(traits::is_move_bitcopying<ErasedType>::value, "ErasedType must be TriviallyCopyable or MoveBitcopying");
261     static_assert(alignof(ErasedType) <= sizeof(ErasedType), "ErasedType must not be over-aligned");
262     ErasedType value;
263     char padding[N];
padded_erasure_objectdetail::padded_erasure_object264     constexpr explicit padded_erasure_object(const ErasedType &v) noexcept
265         : value(v)
266         , padding{}
267     {
268     }
269   };
270 
erasure_cast(const From & from)271   template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && (sizeof(To) == sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(from); }
272 
erasure_cast(const From & from)273   template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && is_static_castable<To, From>::value && (sizeof(To) < sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept { return static_cast<To>(bit_cast<erasure_integer_type<From, To>>(from)); }
274 
erasure_cast(const From & from)275   template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && is_static_castable<To, From>::value && (sizeof(To) > sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept { return bit_cast<To>(static_cast<erasure_integer_type<To, From>>(from)); }
276 
erasure_cast(const From & from)277   template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && !is_static_castable<To, From>::value && (sizeof(To) < sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept
278   {
279     return bit_cast<padded_erasure_object<To, sizeof(From) - sizeof(To)>>(from).value;
280   }
281 
erasure_cast(const From & from)282   template <class To, class From, typename std::enable_if<is_erasure_castable<To, From>::value && !is_static_castable<To, From>::value && (sizeof(To) > sizeof(From)), bool>::type = true> constexpr To erasure_cast(const From &from) noexcept
283   {
284     return bit_cast<To>(padded_erasure_object<From, sizeof(To) - sizeof(From)>{from});
285   }
286 }  // namespace detail
287 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
288 
289 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_FATAL
290 #ifdef BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX
291 #error If BOOST_OUTCOME_SYSTEM_ERROR2_NOT_POSIX is defined, you must define your own BOOST_OUTCOME_SYSTEM_ERROR2_FATAL implementation!
292 #endif
293 #include <cstdlib>  // for abort
294 #ifdef __APPLE__
295 #include <unistd.h>  // for write
296 #endif
297 
298 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
299 namespace detail
300 {
301   namespace avoid_stdio_include
302   {
303 #if !defined(__APPLE__) && !defined(_MSC_VER)
304     extern "C" ptrdiff_t write(int, const void *, size_t);
305 #elif defined(_MSC_VER)
306     extern ptrdiff_t write(int, const void *, size_t);
307 #if defined(_WIN64)
308 #pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YA_JHPEBX_K@Z=write")
309 #else
310 #pragma comment(linker, "/alternatename:?write@avoid_stdio_include@detail@system_error2@@YAHHPBXI@Z=_write")
311 #endif
312 #endif
313   }  // namespace avoid_stdio_include
do_fatal_exit(const char * msg)314   inline void do_fatal_exit(const char *msg)
315   {
316     using namespace avoid_stdio_include;
317     write(2 /*stderr*/, msg, cstrlen(msg));
318     write(2 /*stderr*/, "\n", 1);
319     abort();
320   }
321 }  // namespace detail
322 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
323 //! Prints msg to stderr, and calls `std::terminate()`. Can be overriden via predefinition.
324 #define BOOST_OUTCOME_SYSTEM_ERROR2_FATAL(msg) ::BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::detail::do_fatal_exit(msg)
325 #endif
326 
327 #endif
328