Stroika Library 3.0d20
 
Loading...
Searching...
No Matches
StdCompat.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Common_StdCompat_h_
5#define _Stroika_Foundation_Common_StdCompat_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <bit>
10#include <cmath>
11#include <compare>
12#include <cstdarg>
13#include <ranges>
14
15#if __cpp_lib_expected
16#include <expected>
17#else
18#include <variant>
19#endif
20
21// Various kooky constraints
22// (1) clang++15/16 don't set __cpp_lib_format, so cannot check __cpp_lib_format >= 201907 instead check __has_include(<format>)
23// (2) has_include <format> false positives on some versions of XCode, and no reason to even build qStroika_HasComponent_fmtlib unless
24// its needed, so check it first
25
26#if qStroika_HasComponent_fmtlib
27#include <fmt/chrono.h>
28#include <fmt/format.h>
29#include <fmt/xchar.h>
30#elif __has_include(<format>)
31#include <format>
32#endif
33
34/**
35 * \file
36 *
37 * The purpose of this module is to define any std c++ functions/classes etc - which may not be provided by the
38 * current std c++ library (often because not compiled with appropriate --std=... flag) - and/or because its
39 * an old compiler.
40 *
41 * This doesn't strictly violate any rules about sticking stuff into namespace std - cuz we don't. That's why we use a
42 * separate namespace (that often just indirects to the namespace std - where the function/class is defined already).
43 *
44 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
45 *
46 */
47
48namespace Stroika::Foundation::Common::StdCompat {
49
50 /**
51 * By default include all of std, but throw in selected missing things from some implementations.
52 */
53 using namespace std;
54
55#if qStroika_HasComponent_fmtlib
56#define qStroika_Foundation_Characters_FMT_PREFIX_ fmt
57#elif __has_include(<format>)
58#define qStroika_Foundation_Characters_FMT_PREFIX_ std
59#else
60 static_assert (false, "Stroika v3 requires some std::format compatible library - if building with one lacking builtin std::format, "
61 "configure --fmtlib use");
62#endif
63
64 /**
65 * To allow interop between std::format and fmt(fmtlib)::format, publish the names into the namespace 'Stroika::Foundation::Common::StdCompat' and use those.
66 * Lose this once I can fully depend upon std::format... --LGP 2024-03-12
67 */
68 using qStroika_Foundation_Characters_FMT_PREFIX_::basic_format_parse_context;
69 using qStroika_Foundation_Characters_FMT_PREFIX_::format;
70 using qStroika_Foundation_Characters_FMT_PREFIX_::format_args;
71 using qStroika_Foundation_Characters_FMT_PREFIX_::format_error;
72 using qStroika_Foundation_Characters_FMT_PREFIX_::format_string;
73 using qStroika_Foundation_Characters_FMT_PREFIX_::format_to;
74 using qStroika_Foundation_Characters_FMT_PREFIX_::make_format_args;
75 using qStroika_Foundation_Characters_FMT_PREFIX_::make_wformat_args;
76 using qStroika_Foundation_Characters_FMT_PREFIX_::vformat;
77 using qStroika_Foundation_Characters_FMT_PREFIX_::wformat_args;
78 using qStroika_Foundation_Characters_FMT_PREFIX_::wformat_string;
79
80#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23 /*vis studio uses _HAS_CXX23 */
81 template <class T, class CharT>
82 concept formattable = std::formattable<T, CharT>;
83#else
84 namespace Private_ {
85 template <class _CharT>
86 struct _Phony_fmt_iter_for {
87 using difference_type = ptrdiff_t;
88 _CharT& operator* () const;
89 _Phony_fmt_iter_for& operator++ ();
90 _Phony_fmt_iter_for operator++ (int);
91 };
92 // _Formatter = typename _Context needed for clang++-15 (compiler bug but no BWA declaration for now...BWA in middle of BWA ;-))
93 template <class _Ty, class _Context, class _Formatter = typename _Context::template formatter_type<remove_const_t<_Ty>>>
94 concept _Formattable_with = semiregular<_Formatter> && requires (_Formatter& __f, const _Formatter& __cf, _Ty&& __t, _Context __fc,
95 basic_format_parse_context<typename _Context::char_type> __pc) {
96 { __f.parse (__pc) } -> same_as<typename decltype (__pc)::iterator>;
97 { __cf.format (__t, __fc) } -> same_as<typename _Context::iterator>;
98 };
99 }
100 template <class T, class CharT>
101 concept formattable =
102 Private_::_Formattable_with<remove_reference_t<T>, qStroika_Foundation_Characters_FMT_PREFIX_::basic_format_context<Private_::_Phony_fmt_iter_for<CharT>, CharT>>;
103#endif
104
105 /**
106 * Workaround absence of bit_cast in MacOS XCode 14 (which we support with Stroika v3)
107 */
108#if __cpp_lib_bit_cast >= 201806L
109 using std::bit_cast;
110#else
111 template <class To, class From>
112 inline To bit_cast (const From& src) noexcept
113 requires (sizeof (To) == sizeof (From) && std::is_trivially_copyable_v<From> && std::is_trivially_copyable_v<To>)
114 {
115 static_assert (std::is_trivially_constructible_v<To>, "This implementation additionally requires "
116 "destination type to be trivially constructible");
117 To dst;
118 std::memcpy (&dst, &src, sizeof (To));
119 return dst;
120 }
121#endif
122
123 /**
124 * Workaround absence of byteswap gcc up to version 12, and clang (up to 14).
125 */
126#if __cpp_lib_byteswap >= 202110L
127 using std::byteswap;
128#else
129 template <class T>
130 inline T byteswap (T n) noexcept
131 {
132 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Warray-bounds\"");
133 static_assert (std::has_unique_object_representations_v<T>, "T may not have padding bits");
134 auto value_representation = bit_cast<array<byte, sizeof (T)>> (n);
135 for (size_t i = 0; i < value_representation.size () / 2; ++i) {
136 swap (value_representation[i], value_representation[value_representation.size () - i]);
137 }
138 return bit_cast<T> (value_representation);
139 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Warray-bounds\"");
140 }
141#endif
142
143 /**
144 * workaround qCompilerAndStdLib_fpclasifyEtcOfInteger_Buggy
145 */
146 template <typename T>
147#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _MSVC_LANG >= kStrokia_Foundation_Common_cplusplus_23
148 constexpr
149#else
150 inline
151#endif
152 bool
153 isinf (T v) noexcept
154 {
155#if qCompilerAndStdLib_fpclasifyEtcOfInteger_Buggy
156 if constexpr (integral<T>) {
157 return false; // needed for vis stud
158 }
159 else
160#endif
161 return std::isinf (v);
162 }
163
164 /**
165 * workaround qCompilerAndStdLib_fpclasifyEtcOfInteger_Buggy
166 */
167 template <typename T>
168#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23
169 constexpr
170#else
171 inline
172#endif
173 bool
174 isnan (T v) noexcept
175 {
176#if qCompilerAndStdLib_fpclasifyEtcOfInteger_Buggy
177 if constexpr (integral<T>) {
178 return false; // needed for vis stud
179 }
180 else
181#endif
182 return std::isnan (v);
183 }
184
185#if qCompilerAndStdLib_stdlib_compare_three_way_present_but_Buggy
186 struct compare_three_way {
187 // NOTE - this workaround is GENERALLY INADEQUATE, but is adequate for my current use in Stroika -- LGP 2022-11-01
188 template <typename LT, typename RT>
189 constexpr auto operator() (LT&& lhs, RT&& rhs) const
190 {
191 using CT = common_type_t<LT, RT>;
192 if (equal_to<CT>{}(forward<LT> (lhs), forward<RT> (rhs))) {
193 return strong_ordering::equal;
194 }
195 return less<CT>{}(forward<LT> (lhs), forward<RT> (rhs)) ? strong_ordering::less : strong_ordering::greater;
196 }
197 using is_transparent = void;
198 };
199#else
200 using compare_three_way = std::compare_three_way;
201#endif
202
203 /**
204 * Wrap a simplified version of std::unexpected, cuz handy even if c++23 not present
205 */
206#if __cpp_lib_expected
207 template <typename T>
208 using unexpected = std::unexpected<T>;
209 template <typename T, typename E>
210 using expected = std::expected<T, E>;
211#else
212 template <typename T>
214 public:
215 template <typename _UError = T>
216 requires (!is_same_v<remove_cvref_t<_UError>, unexpected> && !is_same_v<remove_cvref_t<_UError>, in_place_t> && is_constructible_v<T, _UError>)
217 constexpr explicit unexpected (_UError&& _Unex)
218 : _Unexpected (forward<_UError> (_Unex))
219 {
220 }
221 template <typename... _Args>
222 requires is_constructible_v<T, _Args...>
223 constexpr explicit unexpected (in_place_t, _Args&&... _Vals)
224 : _Unexpected (forward<_Args> (_Vals)...)
225 {
226 }
227 template <typename _Uty, typename... _Args>
228 requires is_constructible_v<T, initializer_list<_Uty>&, _Args...>
229 constexpr explicit unexpected (in_place_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals)
230 : _Unexpected (_Ilist, forward<_Args> (_Vals)...)
231 {
232 }
233
234 constexpr const T& error () const& noexcept
235 {
236 return _Unexpected;
237 }
238 constexpr T& error () & noexcept
239 {
240 return _Unexpected;
241 }
242 constexpr const T&& error () const&& noexcept
243 {
244 return std::move (_Unexpected);
245 }
246 constexpr T&& error () && noexcept
247 {
248 return std::move (_Unexpected);
249 }
250
251 constexpr void swap (unexpected& _Other)
252 {
253 using std::swap;
254 swap (_Unexpected, _Other._Unexpected); // intentional ADL
255 }
256
257 friend constexpr void swap (unexpected& _Left, unexpected& _Right)
258 requires is_swappable<T>::value // TRANSITION, /permissive needs ::value
259 {
260 _Left.swap (_Right);
261 }
262
263 template <class _UErr>
264 friend constexpr bool operator== (const unexpected& _Left, const unexpected<_UErr>& _Right)
265 {
266 return _Left._Unexpected == _Right.error ();
267 }
268
269 private:
270 T _Unexpected;
271 };
272 template <typename T>
274
275 /**
276 * Wrap a simplified version of std::expected, cuz handy even if c++23 not present
277 *
278 * \note STILL VERY ROUGH DRAFT - IN USE - BUT NOT SURE ITS RIGHT (cuz mine works but delegating to
279 * MSFT one not working, so maybe mine wrong).
280 */
281 template <typename T, typename E>
282 class expected;
283 template <typename T, typename E>
284 class expected {
285 public:
286 using value_type = T;
287 using error_type = E;
289
290 constexpr expected () noexcept = default;
291 constexpr expected (const expected&) noexcept = default;
292 constexpr expected (T v)
293 : fData_{v}
294 {
295 }
296 constexpr expected (const unexpected_type& e)
297 : fData_{e.error ()}
298 {
299 }
300 template <typename T1, typename E1>
301 constexpr expected (const expected<T1, E1>& e)
302 {
303 if (e) {
304 fData_ = e.value ();
305 }
306 else {
307 fData_ = e.error ();
308 }
309 }
310 explicit operator bool () const noexcept
311 {
312 return std::get_if<T> (&fData_) != nullptr;
313 }
314 T operator* () const
315 {
316 return value ();
317 }
318 T value () const
319 {
320 return get<T> (fData_);
321 }
322 E error () const
323 {
324 return get<E> (fData_);
325 }
326
327 private:
328 variant<T, E> fData_;
329 };
330#endif
331
332}
333
334/*
335 ********************************************************************************
336 ***************************** Implementation Details ***************************
337 ********************************************************************************
338 */
339#include "StdCompat.inl"
340
341#endif /*_Stroika_Foundation_Common_StdCompat_h_*/
constexpr bool isnan(T v) noexcept
Definition StdCompat.h:174
To bit_cast(const From &src) noexcept
Definition StdCompat.h:112
constexpr bool isinf(T v) noexcept
Definition StdCompat.h:153
STL namespace.