File: CommonBase.h

package info (click to toggle)
jazz2-native 3.5.0-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid
  • size: 16,912 kB
  • sloc: cpp: 172,557; xml: 113; python: 36; makefile: 5; sh: 2
file content (634 lines) | stat: -rw-r--r-- 25,786 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
#pragma once

#if defined(__EMSCRIPTEN__)
#	define DEATH_TARGET_EMSCRIPTEN
#elif defined(_WIN32)
#	define DEATH_TARGET_WINDOWS
#elif defined(__ANDROID__)
#	define DEATH_TARGET_ANDROID
#elif defined(__APPLE__)
#	include <TargetConditionals.h>
#	define DEATH_TARGET_APPLE
#	if TARGET_OS_IPHONE
#		define DEATH_TARGET_IOS
#	endif
#	if TARGET_IPHONE_SIMULATOR
#		define DEATH_TARGET_IOS
#		define DEATH_TARGET_IOS_SIMULATOR 
#	endif
#elif defined(__unix__) || defined(__linux__)
#	define DEATH_TARGET_UNIX
#endif

// First two is GCC/Clang for 32/64-bit, second two is MSVC 32/64-bit, last one is for ARM64 Emulation Compatible.
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC)
#	define DEATH_TARGET_ARM

// First two is GCC/Clang for 32/64-bit, second two is MSVC 32/64-bit
#elif defined(__i386) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64)
#	define DEATH_TARGET_X86

// First two is GCC/Clang, third is MSVC. Not sure about 64-bit MSVC.
#elif defined(__powerpc__) || defined(__powerpc64__) || defined(_M_PPC)
#	define DEATH_TARGET_POWERPC

// This is for both RISC-V 32-bit and 64-bit.
#elif defined(__riscv)
#	define DEATH_TARGET_RISCV

// WebAssembly (on Emscripten). Old pure asm.js toolchains did not define this, recent Emscripten does that even with `-s WASM=0`.
#elif defined(__wasm__)
#	define DEATH_TARGET_WASM

#endif

// Sanity checks. This might happen when using Emscripten-compiled code with native compilers, at which point we should just die.
#if defined(DEATH_TARGET_EMSCRIPTEN) && (defined(DEATH_TARGET_X86) || defined(DEATH_TARGET_ARM) || defined(DEATH_TARGET_POWERPC) || defined(DEATH_TARGET_RISCV))
#	error DEATH_TARGET_X86 / _ARM / _POWERPC / _RISCV defined on Emscripten
#endif

// 64-bit WebAssembly macro was tested by passing -m64 to emcc
#if !defined(__x86_64) && !defined(_M_X64) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__powerpc64__) && !defined(__wasm64__) && !defined(DOXYGEN_GENERATING_OUTPUT)
#	define DEATH_TARGET_32BIT
#endif

/**
	@brief C++ standard version

	Expands to `__cplusplus` macro on all sane compilers; on MSVC uses `_MSVC_LANG` if defined
	(since Visual Studio 2015 Update 3), otherwise reports C++11. The returned version is:

	-   @cpp 201103 @ce when C++11 is used
	-   @cpp 201402 @ce when C++14 is used
	-   @cpp 201703 @ce when C++17 is used
	-   @cpp 202002 @ce when C++20 is used
	-   greater than @cpp 202002 @ce when C++2b is used

	Note that compilers that don't have full support for given standard may not return the exact value,
	in which case it's recommended to check that the reported value is *greater* than the previous standard,
	for example @cpp #if DEATH_CXX_STANDARD > 201703 @ce to test whether compiling as C++20.
*/
#if defined(_MSC_VER)
#	if defined(_MSVC_LANG)
#		define DEATH_CXX_STANDARD _MSVC_LANG
#	else
#		define DEATH_CXX_STANDARD 201103L
#	endif
#else
#	define DEATH_CXX_STANDARD __cplusplus
#endif

// <ciso646> is deprecated and replaced by <version> in C++20, but some compilers warn even in C++17
#if DEATH_CXX_STANDARD >= 201703L && defined(__has_include)
#	if __has_include(<version>)
#		include <version>
#	else
#		include <ciso646>
#	endif
#else
#	include <ciso646>
#endif
#if defined(_LIBCPP_VERSION)
#	define DEATH_TARGET_LIBCXX
#elif defined(_CPPLIB_VER)
#	define DEATH_TARGET_DINKUMWARE
#elif defined(__GLIBCXX__)
#	define DEATH_TARGET_LIBSTDCXX
// GCC's <ciso646> provides the __GLIBCXX__ macro only since 6.1, so on older versions we'll try to get it from <bits/c++config.h>
#elif defined(__has_include)
#	if __has_include(<bits/c++config.h>)
#		include <bits/c++config.h>
#		if defined(__GLIBCXX__)
#			define DEATH_TARGET_LIBSTDCXX
#		endif
#	endif
// GCC < 5.0 doesn't have __has_include, so on these versions we'll just assume it's libstdc++ as I don't think those versions
// are used with anything else nowadays anyway. Clang reports itself as GCC 4.4, so exclude that one.
#elif defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
#	define DEATH_TARGET_LIBSTDCXX
#else
// Otherwise no idea
#endif

#if defined(__GNUC__)
#	define DEATH_TARGET_GCC
#endif

#if defined(__clang__)
#	define DEATH_TARGET_CLANG
#endif

#if defined(__clang__) && defined(_MSC_VER)
#	define DEATH_TARGET_CLANG_CL
#endif

#if defined(__clang__) && defined(__apple_build_version__)
#	define DEATH_TARGET_APPLE_CLANG
#endif

#if defined(__INTEL_LLVM_COMPILER)
#	define DEATH_TARGET_INTEL_LLVM
#endif

#if defined(_MSC_VER)
#	define DEATH_TARGET_MSVC
#	if _MSC_VER < 1910
#		define DEATH_MSVC2015_COMPATIBILITY
#	endif
#	if _MSC_VER < 1920
#		define DEATH_MSVC2017_COMPATIBILITY
#	endif
#	if _MSC_VER < 1930
#		define DEATH_MSVC2019_COMPATIBILITY
#	endif
#endif

#if defined(__CYGWIN__)
#	define DEATH_TARGET_CYGWIN
#elif defined(__MINGW32__)
#	define DEATH_TARGET_MINGW
#endif

// First checking the GCC/Clang built-in, if available. As a fallback do an architecture-based check, which is mirrored
// from SDL_endian.h. Doing this *properly* would mean we can't decide this at compile time as some architectures allow
// switching endianness at runtime (and worse, have per-page endianness). So let's pretend we never saw this article:
// https://en.wikipedia.org/wiki/Endianness#Bi-endianness
#if defined(__BYTE_ORDER__)
#	if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#		define DEATH_TARGET_BIG_ENDIAN
#	elif __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
#		error What kind of endianness is this?
#	endif
#elif defined(__hppa__) || \
	defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
	(defined(__MIPS__) && defined(__MIPSEB__)) || \
	defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
	defined(__sparc__)
#	define DEATH_TARGET_BIG_ENDIAN
#endif

// Compile-time CPU feature detection
#if defined(DEATH_TARGET_X86)

// SSE on GCC: https://stackoverflow.com/a/28939692
#if defined(DEATH_TARGET_GCC)
#	if defined(__SSE2__)
#		define DEATH_TARGET_SSE2
#	endif
#	if defined(__SSE3__)
#		define DEATH_TARGET_SSE3
#	endif
#	if defined(__SSSE3__)
#		define DEATH_TARGET_SSSE3
#	endif
#	if defined(__SSE4_1__)
#		define DEATH_TARGET_SSE41
#	endif
#	if defined(__SSE4_2__)
#		define DEATH_TARGET_SSE42
#	endif

// On MSVC: https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
#elif defined(DEATH_TARGET_MSVC)
// _M_IX86_FP is defined only on 32-bit, 64-bit has SSE2 always (so we need to detect 64-bit instead: https://stackoverflow.com/a/18570487)
#	if (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(_M_AMD64) || defined(_M_X64)
#		define DEATH_TARGET_SSE2
#	endif
// On MSVC there's no way to detect SSE3 and newer, these are only implied by AVX as far as I can tell
#	if defined(__AVX__)
#		define DEATH_TARGET_SSE3
#		define DEATH_TARGET_SSSE3
#		define DEATH_TARGET_SSE41
#		define DEATH_TARGET_SSE42
#	endif
#endif

// Both GCC and MSVC have the same macros for AVX, AVX2 and AVX512F
#if defined(DEATH_TARGET_GCC) || defined(DEATH_TARGET_MSVC)
#	if defined(__AVX__)
#		define DEATH_TARGET_AVX
#	endif
#	if defined(__AVX2__)
#		define DEATH_TARGET_AVX2
#	endif
#	if defined(__AVX512F__)
#		define DEATH_TARGET_AVX512F
#	endif
#endif

// POPCNT, LZCNT and BMI1 on GCC, queried wth `gcc -mpopcnt -dM -E - | grep POPCNT`, and equivalent for others.
#if defined(DEATH_TARGET_GCC)
#	if defined(__POPCNT__)
#		define DEATH_TARGET_POPCNT
#	endif
#	if defined(__LZCNT__)
#		define DEATH_TARGET_LZCNT
#	endif
#	if defined(__BMI__)
#		define DEATH_TARGET_BMI1
#	endif
#	if defined(__BMI2__)
#		define DEATH_TARGET_BMI2
#	endif

// There doesn't seem to be any equivalent on MSVC, https://github.com/kimwalisch/libpopcnt assumes POPCNT is on x86 MSVC
// always, and LZCNT has encoding compatible with BSR so if not available it'll not crash but produce wrong results, sometimes.
// Enabling them always feels a bit too much, so instead going with what Clang-CL uses. There /arch:AVX matches -march=sandybridge:
// https://github.com/llvm/llvm-project/blob/6542cb55a3eb115b1c3592514590a19987ffc498/clang/lib/Driver/ToolChains/Arch/X86.cpp#L46-L58
// and `echo | clang -march=sandybridge -dM -E -` lists only POPCNT, while /arch:AVX2 matches -march=haswell, which lists also LZCNT, BMI and BMI2.
#elif defined(DEATH_TARGET_MSVC)
// For extra robustness on Clang-CL check the macros explicitly -- as with other AVX+ intrinsics, these are only included
// if the corresponding macro is defined as well. Failing to do so would mean the DEATH_ENABLE_AVX_{POPCNT,LZCNT,BMI1} macros
// are defined always, incorrectly implying presence of these intrinsics.
#	if defined(__AVX__)
#		if !defined(DEATH_TARGET_CLANG_CL) || defined(__POPCNT__)
#			define DEATH_TARGET_POPCNT
#		endif
#	endif
#	if defined(__AVX2__)
#		if !defined(DEATH_TARGET_CLANG_CL) || defined(__LZCNT__)
#			define DEATH_TARGET_LZCNT
#		endif
#		if !defined(DEATH_TARGET_CLANG_CL) || defined(__BMI__)
#			define DEATH_TARGET_BMI1
#		endif
#		if !defined(DEATH_TARGET_CLANG_CL) || defined(__BMI2__)
#			define DEATH_TARGET_BMI2
#		endif
#	endif
#endif

// On GCC, F16C and FMA have its own define, on MSVC FMA is implied by /arch:AVX2 (https://docs.microsoft.com/en-us/cpp/build/reference/arch-x86),
// no mention of F16C but https://walbourn.github.io/directxmath-f16c-and-fma/ says it's like that so I'll believe that.
// However, comments below https://stackoverflow.com/a/50829580 say there is a Via processor with AVX2 but no FMA,
// so then the __AVX2__ check isn't really bulletproof. Use runtime detection where possible, please.
#if defined(DEATH_TARGET_GCC)
#	if defined(__F16C__)
#		define DEATH_TARGET_AVX_F16C
#	endif
#	if defined(__FMA__)
#		define DEATH_TARGET_AVX_FMA
#	endif
#elif defined(DEATH_TARGET_MSVC) && defined(__AVX2__)
// On Clang-CL /arch:AVX2 matches -march=haswell:
// https://github.com/llvm/llvm-project/blob/6542cb55a3eb115b1c3592514590a19987ffc498/clang/lib/Driver/ToolChains/Arch/X86.cpp#L46-L58
// And `echo | clang -march=haswell -dM -E -` lists both F16C and FMA. However, for robustness, check the macros
// explicitly -- as with other AVX+ intrinsics on Clang-CL, these are only included if the corresponding macro is defined
// as well. Failing to do so would mean the DEATH_ENABLE_AVX_{16C,FMA} macros are defined always, incorrectly implying
// presence of these intrinsics.
#	if !defined(DEATH_TARGET_CLANG_CL) || defined(__F16C__)
#		define DEATH_TARGET_AVX_F16C
#	endif
#	if !defined(DEATH_TARGET_CLANG_CL) || defined(__FMA__)
#		define DEATH_TARGET_AVX_FMA
#	endif
#endif

// https://stackoverflow.com/a/37056771, confirmed on Android NDK Clang that __ARM_NEON is indeed still set.
// For MSVC, according to https://docs.microsoft.com/en-us/cpp/intrinsics/arm-intrinsics I would assume that since they
// use a standard header, they also expose the standard macro name, even though not listed among their predefined macros.
#elif defined(DEATH_TARGET_ARM)
#	if defined(__ARM_NEON)
#		define DEATH_TARGET_NEON
// NEON FMA is available only if __ARM_FEATURE_FMA is defined and some bits of __ARM_NEON_FP as well (ARM C Language Extensions
// 1.1, ยง6.5.5: https://developer.arm.com/documentation/ihi0053/b/). On ARM64 NEON is implicitly supported and __ARM_NEON_FP
// might not be defined (Android Clang defines it but GCC 9 on Ubuntu ARM64 not), so check for __aarch64__ as well.
#		if defined(__ARM_FEATURE_FMA) && (__ARM_NEON_FP || defined(__aarch64__))
#			define DEATH_TARGET_NEON_FMA
#		endif
// There's no linkable documentation for anything and the PDF is stupid. But, given the FP16 instructions implemented
// in GCC and Clang are guarded by this macro, it should be alright: https://gcc.gnu.org/legacy-ml/gcc-patches/2016-06/msg00460.html
#		if defined(__ARM_FEATURE_FP16_VECTOR_ARITHMETIC)
#			define DEATH_TARGET_NEON_FP16
#		endif
#	endif

// Undocumented, checked via `echo | em++ -x c++ -dM -E - -msimd128`. Restricting to the finalized SIMD variant,
// which is since Clang 13: https://github.com/llvm/llvm-project/commit/502f54049d17f5a107f833596fb2c31297a99773
// Emscripten 2.0.13 sets Clang 13 as the minimum, however it doesn't imply that emsdk 2.0.13 actually contains the final
// Clang 13. That's only since 2.0.18, thus to avoid nasty issues we have to check Emscripten version as well :(
// https://github.com/emscripten-core/emscripten/commit/deab7783df407b260f46352ffad2a77ca8fb0a4c
#elif defined(DEATH_TARGET_WASM)
#	if defined(__wasm_simd128__) && __clang_major__ >= 13 && __EMSCRIPTEN_major__*10000 + __EMSCRIPTEN_minor__*100 + __EMSCRIPTEN_tiny__ >= 20018
#		define DEATH_TARGET_SIMD128
#	endif
#endif

// Kill switch for when presence of a sanitizer is detected and DEATH_CPU_USE_IFUNC is enabled. Unfortunately in our case the
// __attribute__((no_sanitize_address)) workaround as described on https://github.com/google/sanitizers/issues/342 doesn't
// work / can't be used because it would mean marking basically everything including the actual implementation that's being dispatched to.
#if defined(DEATH_CPU_USE_IFUNC)
#	if defined(__has_feature)
#		if __has_feature(address_sanitizer) || __has_feature(thread_sanitizer) || __has_feature(memory_sanitizer) || __has_feature(undefined_behavior_sanitizer)
#			define __DEATH_SANITIZER_IFUNC_DETECTED
#		endif
#	elif defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__)
#		define __DEATH_SANITIZER_IFUNC_DETECTED
#	endif
#	if defined(__DEATH_SANITIZER_IFUNC_DETECTED)
#		 error The library was built with DEATH_CPU_USE_IFUNC, which is incompatible with sanitizers. Rebuild without this option or disable sanitizers.
#	endif
#endif

/**
	@brief Whether `long double` has the same precision as `double`

	Defined on platforms where the @cpp long double @ce type has a 64-bit precision instead of 80-bit,
	thus same as @cpp double @ce. It's the case for @ref DEATH_TARGET_MSVC "MSVC" ([source](https://docs.microsoft.com/en-us/previous-versions/9cx8xs15(v=vs.140))),
	32-bit @ref DEATH_TARGET_ANDROID "Android", @ref DEATH_TARGET_EMSCRIPTEN "Emscripten"
	and @ref DEATH_TARGET_APPLE "Mac" (but not @ref DEATH_TARGET_IOS "iOS") with @ref DEATH_TARGET_ARM "ARM"
	processors. Emscripten is a bit special because it's @cpp long double @ce is sometimes 80-bit, but
	its precision differs from the 80-bit representation elsewhere, so it's always treated as 64-bit.
	Note that even though the type size and precision may be the same, these are still two distinct types,
	similarly to how @cpp int @ce and @cpp signed int @ce behave the same but are treated as different types.
*/
#if defined(DEATH_TARGET_MSVC) || (defined(DEATH_TARGET_ANDROID) && !__LP64__) || defined(DEATH_TARGET_EMSCRIPTEN) || (defined(DEATH_TARGET_APPLE) && !defined(DEATH_TARGET_IOS) && defined(DEATH_TARGET_ARM)) || defined(DOXYGEN_GENERATING_OUTPUT)
#	define DEATH_LONG_DOUBLE_SAME_AS_DOUBLE
#endif

/**
	@brief Whether source location built-ins are supported

	Defined if compiler-specific builtins used to implement the C++20 
	@m_class{m-doc-external} [std::source_location](https://en.cppreference.com/w/cpp/utility/source_location)
	feature are available. Defined on GCC at least since version 4.8, Clang 9+ and MSVC 2019 16.6 and newer;
	on all three they're present also in the C++11 mode.
*/
#if (defined(DEATH_TARGET_GCC) && !defined(DEATH_TARGET_CLANG)) || (defined(DEATH_TARGET_CLANG) && ((defined(__apple_build_version__) && __clang_major__ >= 12) || (!defined(__apple_build_version__) && __clang_major__ >= 9))) || (defined(DEATH_TARGET_MSVC) && _MSC_VER >= 1926) || defined(DOXYGEN_GENERATING_OUTPUT)
#	define DEATH_SOURCE_LOCATION_BUILTINS_SUPPORTED
#endif

/**
	@brief Deprecation mark

	Marked function, class or typedef will emit deprecation warning on supported compilers (GCC, Clang, MSVC).
*/
#if !defined(DEATH_DEPRECATED) || defined(DOXYGEN_GENERATING_OUTPUT)
#	if defined(DEATH_TARGET_GCC) || defined(DEATH_TARGET_CLANG)
#		define DEATH_DEPRECATED(message) __attribute((deprecated(message)))
#	elif defined(DEATH_TARGET_MSVC)
#		define DEATH_DEPRECATED(message) __declspec(deprecated(message))
#	else
#		define DEATH_DEPRECATED(message)
#	endif
#endif

/**
	@brief Begin code section with deprecation warnings ignored

	Suppresses compiler warnings when using a deprecated API (GCC, Clang, MSVC). Useful when testing or writing
	APIs that depend on deprecated functionality. In order to avoid warning suppressions to leak, for every
	@ref DEATH_IGNORE_DEPRECATED_PUSH there has to be a corresponding @ref DEATH_IGNORE_DEPRECATED_POP.
*/
#if !defined(DEATH_IGNORE_DEPRECATED_PUSH)
#	if defined(DEATH_TARGET_CLANG)
#		define DEATH_IGNORE_DEPRECATED_PUSH _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") _Pragma("GCC diagnostic ignored \"-W#pragma-messages\"")
#	elif defined(DEATH_TARGET_GCC)
#		define DEATH_IGNORE_DEPRECATED_PUSH _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#	elif defined(DEATH_TARGET_MSVC)
#		define DEATH_IGNORE_DEPRECATED_PUSH __pragma(warning(push)) __pragma(warning(disable: 4996))
#	else
#		define DEATH_IGNORE_DEPRECATED_PUSH
#	endif
#endif

/**
	@brief End code section with deprecation warnings ignored

	See @ref DEATH_IGNORE_DEPRECATED_PUSH for more information.
*/
#if !defined(DEATH_IGNORE_DEPRECATED_POP)
#	if defined(DEATH_TARGET_GCC)
#		define DEATH_IGNORE_DEPRECATED_POP _Pragma("GCC diagnostic pop")
#	elif defined(DEATH_TARGET_MSVC)
#		define DEATH_IGNORE_DEPRECATED_POP __pragma(warning(pop))
#	else
#		define DEATH_IGNORE_DEPRECATED_POP
#	endif
#endif

/**
	@brief Unused variable mark

	Putting this before unused variable will suppress compiler warning about it being unused.
	If possible, use @cpp static_cast<void>(var) @ce or nameless function parameters instead.
*/
#if !defined(DEATH_UNUSED)
#	if defined(DEATH_TARGET_GCC) && !defined(DEATH_TARGET_CLANG) && __GNUC__ >= 10
#		define DEATH_UNUSED [[maybe_unused]]
#	elif defined(DEATH_TARGET_GCC) || defined(DEATH_TARGET_CLANG_CL)
#		define DEATH_UNUSED __attribute__((__unused__))
#	elif defined(DEATH_TARGET_MSVC)
#		define DEATH_UNUSED __pragma(warning(suppress:4100))
#	else
#		define DEATH_UNUSED
#	endif
#endif

/**
	@brief Switch case fall-through

	Suppresses a warning about a @cpp case @ce fallthrough in a @cpp switch @ce.
	Expected to be put at a place where a @cpp break; @ce would usually be
*/
#if !defined(DEATH_FALLTHROUGH)
#	if (defined(DEATH_TARGET_MSVC) && _MSC_VER >= 1926 && DEATH_CXX_STANDARD >= 201703) || (defined(DEATH_TARGET_GCC) && !defined(DEATH_TARGET_CLANG) && __GNUC__ >= 7)
#		define DEATH_FALLTHROUGH [[fallthrough]];
#	elif defined(DEATH_TARGET_CLANG)
#		define DEATH_FALLTHROUGH [[clang::fallthrough]];
#	else
#		define DEATH_FALLTHROUGH
#	endif
#endif

/**
	@brief Thread-local annotation

	Expands to C++11 @cpp thread_local @ce keyword on all compilers except old Apple Clang,
	where it's defined as @cpp __thread @ce. Note that the pre-standard @cpp __thread @ce has
	some semantic differences, in particular regarding RAII.
*/
#if !defined(DEATH_THREAD_LOCAL)
#	if defined(DEATH_TARGET_APPLE) && defined(__has_feature)
#		if !__has_feature(cxx_thread_local) /* Apple Clang 7.3 says false here */
#			define DEATH_THREAD_LOCAL __thread
#		endif
#	endif
#	if !defined(DEATH_THREAD_LOCAL) /* Assume it's supported otherwise */
#		define DEATH_THREAD_LOCAL thread_local
#	endif
#endif

/**
	@brief C++14 constexpr annotation

	Expands to @cpp constexpr @ce if C++14 or newer standard is enabled and if the compiler implements C++14
	relaxed constexpr rules (which includes GCC 5+, Clang 3.5+ and MSVC 2017+).
*/
#if !defined(DEATH_CONSTEXPR14) || defined(DOXYGEN_GENERATING_OUTPUT)
#	if DEATH_CXX_STANDARD >= 201402 && !defined(DEATH_MSVC2015_COMPATIBILITY)
#		define DEATH_CONSTEXPR14 constexpr
#	else
#		define DEATH_CONSTEXPR14
#	endif
#endif

/**
	@brief C++20 constexpr annotation

	Expands to @cpp constexpr @ce if C++20 or newer standard is enabled and if the compiler implements
	all C++20 constexpr language additions such as trivial default initialization (which includes
	GCC 10+, Clang 10+ and MSVC 2019 19.29+).
*/
#if !defined(DEATH_CONSTEXPR20) || defined(DOXYGEN_GENERATING_OUTPUT)
#	if __cpp_constexpr >= 201907
#		define DEATH_CONSTEXPR20 constexpr
#	else
#		define DEATH_CONSTEXPR20
#	endif
#endif

/**
	@brief Always inline a function

	Stronger than the standard @cpp inline @ce keyword where supported, but even then the compiler
	might decide to not inline the function (for example if it's recursive). Expands to
	@cpp __attribute__((always_inline)) inline @ce on GCC and Clang (both keywords need to be specified,
	[docs](https://gcc.gnu.org/onlinedocs/gcc/extensions-to-the-c-language-family/declaring-attributes-of-functions/common-function-attributes.html#fn-attr-always_inline)),
	to @cpp __forceinline @ce on MSVC ([docs](https://docs.microsoft.com/en-us/cpp/cpp/inline-functions-cpp))
	and to just @cpp inline @ce elsewhere. On GCC and Clang this makes the function inline also in Debug
	mode (`-g`), while on MSVC compiling in Debug (`/Ob0`) always suppresses all inlining.
*/
#if !defined(DEATH_ALWAYS_INLINE)
#	if defined(DEATH_TARGET_GCC)
#		define DEATH_ALWAYS_INLINE __attribute__((always_inline)) inline
#	elif defined(DEATH_TARGET_MSVC)
#		define DEATH_ALWAYS_INLINE __forceinline
#	else
#		define DEATH_ALWAYS_INLINE inline
#	endif
#endif

/**
	@brief Never inline a function

	Prevents the compiler from inlining a function during an optimization pass. Expands to @cpp __attribute__((noinline)) @ce on GCC and Clang
	([docs](https://gcc.gnu.org/onlinedocs/gcc/extensions-to-the-c-language-family/declaring-attributes-of-functions/common-function-attributes.html#fn-attr-noinline)),
	to @cpp __declspec(noinline) @ce on MSVC ([docs](https://docs.microsoft.com/en-us/cpp/cpp/noinline)) and is empty elsewhere.
*/
#if !defined(DEATH_NEVER_INLINE)
#	if defined(DEATH_TARGET_GCC)
#		define DEATH_NEVER_INLINE __attribute__((noinline))
#	elif defined(DEATH_TARGET_MSVC)
#		define DEATH_NEVER_INLINE __declspec(noinline)
#	else
#		define DEATH_NEVER_INLINE
#	endif
#endif

/**
	@brief Hint for compiler that a variable isn't aliased in the current scope

	This macro provides a hint to the compiler that for the lifetime of the pointer, no other pointer
	will be used to access the object it points to.
*/
#if !defined(DEATH_RESTRICT)
#	if defined(__cplusplus)
#		define DEATH_RESTRICT __restrict
#	else
#		define DEATH_RESTRICT restrict
#	endif
#endif

/**
	@brief Hint for compiler to assume a condition

	This macro does not handle the case when the condition isn't @cpp true @ce in any way --- only
	provides a hint to the compiler, possibly improving performance.
*/
#if !defined(DEATH_ASSUME)
#	if defined(DEATH_TARGET_CLANG)
#		define DEATH_ASSUME(condition) __builtin_assume(condition)
#	elif defined(DEATH_TARGET_MSVC)
#		define DEATH_ASSUME(condition) __assume(condition)
#	elif defined(DEATH_TARGET_GCC)
#		if __GNUC__ >= 13
#			define DEATH_ASSUME(condition) __attribute__((assume(condition)))
#		else
#			define DEATH_ASSUME(condition)							\
				do {												\
					if(!(condition)) __builtin_unreachable();		\
				} while(false)
#		endif
#	else
#		define DEATH_ASSUME(condition) do {} while(false)
#	endif
#endif

/**
	@brief Mark an if condition as likely to happen

	Since branch predictors of contemporary CPUs do a good enough job already, the main purpose
	of this macro is to affect assembly generation and instruction cache use in hot loops --- for
	example, when a certain condition is likely to happen each iteration, the compiler may
	put code of the @cpp else @ce branch in a "cold" section of the code, ensuring the more
	probable code path stays in the cache.
*/
#if !defined(DEATH_LIKELY)
#	if (defined(DEATH_TARGET_GCC) && !defined(DEATH_TARGET_CLANG) && __GNUC__ >= 10) || (DEATH_CXX_STANDARD > 201703 && ((defined(DEATH_TARGET_CLANG) && !defined(DEATH_TARGET_APPLE_CLANG) && __clang_major__ >= 12) || (defined(DEATH_TARGET_MSVC) && _MSC_VER >= 1926)))
#		define DEATH_LIKELY(...) (__VA_ARGS__) [[likely]]
#	elif defined(DEATH_TARGET_GCC)
#		define DEATH_LIKELY(...) (__builtin_expect((__VA_ARGS__), 1))
#	else
#		define DEATH_LIKELY(...) (__VA_ARGS__)
#	endif
#endif

/**
	@brief Mark an if condition as unlikely to happen

	An inverse to @ref DEATH_LIKELY(), see its documentation for more information about suggested
	use and expected impact on performance. Useful to mark boundary conditions in tight loops.
*/
#if !defined(DEATH_UNLIKELY)
#	if (defined(DEATH_TARGET_GCC) && !defined(DEATH_TARGET_CLANG) && __GNUC__ >= 10) || (DEATH_CXX_STANDARD > 201703 && ((defined(DEATH_TARGET_CLANG) && !defined(DEATH_TARGET_APPLE_CLANG) && __clang_major__ >= 12) || (defined(DEATH_TARGET_MSVC) && _MSC_VER >= 1926)))
#		define DEATH_UNLIKELY(...) (__VA_ARGS__) [[unlikely]]
#	elif defined(DEATH_TARGET_GCC)
#		define DEATH_UNLIKELY(...) (__builtin_expect((__VA_ARGS__), 0))
#	else
#		define DEATH_UNLIKELY(...) (__VA_ARGS__)
#	endif
#endif

/**
	@brief Passthrough

	Expands to all arguments passed to it. Inverse of @ref DEATH_NOOP().
*/
#if !defined(DEATH_PASSTHROUGH)
#	define DEATH_PASSTHROUGH(...) __VA_ARGS__
#endif

/**
	@brief No-op

	Eats all arguments passed to it. Inverse of @ref DEATH_PASSTHROUGH(). Useful on compilers that
	don't support defining function macros on command line
*/
#if !defined(DEATH_NOOP)
#	define DEATH_NOOP(...)
#endif

#ifndef DOXYGEN_GENERATING_OUTPUT
// Internal macro implementation
#define __DEATH_HELPER_STR(x) #x
#define __DEATH_LINE_STRING_IMPLEMENTATION(...) __DEATH_HELPER_STR(__VA_ARGS__)
#endif

/**
	@brief Line number as a string

	Turns the standard @cpp __LINE__ @ce macro into a string. Useful for example to have correct
	line numbers when embedding GLSL shaders directly in the code.
*/
#define DEATH_LINE_STRING __DEATH_LINE_STRING_IMPLEMENTATION(__LINE__)