HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
macros.hpp
1// Copyright Take Vos 2023.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#if defined(_MSC_BUILD)
8#include <intrin.h>
9#endif
10#include <version>
11#include <exception>
12
13#define HI_OS_WINDOWS 'W'
14#define HI_OS_ANDROID 'A'
15#define HI_OS_LINUX 'L'
16#define HI_OS_MACOS 'M'
17#define HI_OS_IOS 'I'
18
19// W don't use HI_GENERIC for the operating system, because too many things
20// like mmap-file-io, vulkan, window, main-loop depend on this.
21#if defined(_WIN32)
22#define HI_OPERATING_SYSTEM HI_OS_WINDOWS
23#elif defined(__ANDROID__)
24#define HI_OPERATING_SYSTEM HI_OS_ANDROID
25#elif defined(__linux__)
26#define HI_OPERATING_SYSTEM HI_OS_LINUX
27#elif defined(__APPLE__) and defined(__MACH__)
28#include <TargetConditionals.h>
29#if TARGET_IPHONE_SIMULATOR or TARGET_OS_IPHONE or TARGET_OS_MACCATALYST
30#define HI_OPERATING_SYSTEM HI_OS_IOS
31#else
32#define HI_OPERATING_SYSTEM HI_OS_MACOS
33#endif
34#else
35#error "Unknown operating system"
36#endif
37
38#define HI_CC_MSVC 'm'
39#define HI_CC_GCC 'g'
40#define HI_CC_CLANG 'c'
41#define HI_CC_OTHER '-'
42
43#if defined(HI_GENERIC)
44#define HI_COMPILER HI_CC_OTHER
45#elif defined(__clang__)
46#define HI_COMPILER HI_CC_CLANG
47#elif defined(_MSC_BUILD)
48#define HI_COMPILER HI_CC_MSVC
49#elif defined(__GNUC__)
50#define HI_COMPILER HI_CC_GCC
51#else
52#define HI_COMPILER HI_CC_OTHER
53#endif
54
55#define HI_STL_MS 'm'
56#define HI_STL_GNU 'g'
57#define HI_STL_LLVM 'l'
58#define HI_STL_OTHER '-'
59
60#if defined(HI_GENERIC)
61#define HI_STD_LIBRARY HI_STL_OTHER
62#elif defined(__GLIBCXX__)
63#define HI_STD_LIBRARY HI_STL_GNU
64#elif defined(_LIBCPP_VERSION)
65#define HI_STD_LIBRARY HI_STL_LLVM
66#elif defined(_CPPLIB_VER)
67#define HI_STD_LIBRARY HI_STL_MS
68#else
69#define HI_STD_LIBRARY HI_STL_OTHER
70#endif
71
72#define HI_CPU_X86 'i'
73#define HI_CPU_X86_64 'I'
74#define HI_CPU_ARM 'a'
75#define HI_CPU_ARM64 'A'
76#define HI_CPU_OTHER '-'
77
78
79#if defined(HI_GENERIC)
80#define HI_PROCESSOR HI_CPU_OTHER
81
82// MSVC platform detection.
83// - _M_AMD64 determines if the processor is 64 bit, both x86-64 and arm64.
84// - _M_IX86 is not defined on x86-64, but _M_IX86_FP is.
85#elif defined(_M_IX86_FP)
86#if defined(_M_AMD64)
87#define HI_PROCESSOR HI_CPU_X86_64
88#else
89#define HI_PROCESSOR HI_CPU_X86
90#endif
91
92#elif defined(_M_ARM_FP)
93#if defined(_M_AMD64)
94#define HI_PROCESSOR HI_CPU_ARM64
95#else
96#define HI_PROCESSOR HI_CPU_ARM
97#endif
98
99// clang/gcc platform detection
100#elif defined(__amd64__) or defined(__amd64) or defined(__x86_64__) or defined(__x86_64) or defined(_M_AMD64) or defined(_M_X64)
101#define HI_PROCESSOR HI_CPU_X86_64
102#elif defined(__aarch64__) or defined(_M_ARM64)
103#define HI_PROCESSOR HI_CPU_ARM64
104#elif defined(__i386__) or defined(_M_IX86)
105#define HI_PROCESSOR HI_CPU_X86
106#elif defined(__arm__) or defined(__arm) or defined(_ARM) or defined(_M_ARM)
107#define HI_PROCESSOR HI_CPU_ARM
108
109// Fallback if CPU is not detected.
110#else
111#define HI_PROCESSOR HI_CPU_OTHER
112#endif
113
114#ifndef hi_keywords
115#define hi_keywords
116#define hi_export
117#define hi_export_module(...)
118#if defined(__clang__) or defined(__GNUC__)
119#define hi_target(...) [[gnu::target(__VA_ARGS__)]]
120#define hi_no_inline [[gnu::noinline]]
121#define hi_force_inline [[gnu::always_inline]]
122#define hi_restrict __restrict__
123#define hi_warning_push() _Pragma("warning(push)")
124#define hi_warning_pop() _Pragma("warning(push)")
125#define hi_warning_ignore_msvc(...)
126#define hi_warning_ignore_clang(...) _Pragma(hi_stringify(clang diagnostic ignored __VA_ARGS__))
127#define hi_warning_ignore_gcc(...)
128#define hi_no_sanitize_address
129#define hi_assume(...) __builtin_assume(not not(__VA_ARGS__))
130#define hi_assert_break() __builtin_trap()
131#define hi_debug_break() __builtin_debugtrap()
132#elif defined(_MSC_BUILD)
133#define hi_target(...)
134#define hi_no_inline [[msvc::noinline]]
135#define hi_force_inline [[msvc::forceinline]]
136#define hi_restrict __restrict
137#define hi_warning_push() _Pragma("warning( push )")
138#define hi_warning_pop() _Pragma("warning( pop )")
139#define hi_warning_ignore_msvc(...) _Pragma(hi_stringify(warning(disable : __VA_ARGS__)))
140#define hi_warning_ignore_clang(...)
141#define hi_warning_ignore_gcc(...)
142#define hi_no_sanitize_address __declspec(no_sanitize_address)
143#define hi_assume(...) __assume(not not(__VA_ARGS__))
144#define hi_assert_break() __int2c()
145#define hi_debug_break() __debugbreak()
146#else
147#define hi_target(...)
148#define hi_no_inline
149#define hi_force_inline
150#define hi_restrict
151#define hi_warning_push()
152#define hi_warning_pop()
153#define hi_warning_ignore_msvc(...)
154#define hi_warning_ignore_clang(...)
155#define hi_warning_ignore_gcc(...)
156#define hi_no_sanitize_address
157#define hi_assume(...) static_assert(std::convertible_to<decltype(not not(__VA_ARGS__)), bool>)
158#define hi_assert_break() std::terminate()
159#define hi_debug_break() std::terminate()
160#endif
161#endif
162
169#define hi_return_if_valid(expression) \
170 do { \
171 if constexpr (requires { expression; }) { \
172 return expression; \
173 } \
174 } while (false)
175
176// One clang-format off is not enough to stop clang-format to format.
177// clang-format off
178#define hi_num_va_args_impl( \
179 _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
180 _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
181 _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
182 _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
183 _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
184 _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
185 _61,_62,_63,N,...) N
186#define hi_num_va_args_(...) hi_num_va_args_impl(__VA_ARGS__)
187// clang-format on
188
194// clang-format off
195#define hi_num_va_args(...) hi_num_va_args_(__VA_ARGS__ __VA_OPT__(,) \
196 63,62,61,60, \
197 59,58,57,56,55,54,53,52,51,50, \
198 49,48,47,46,45,44,43,42,41,40, \
199 39,38,37,36,35,34,33,32,31,30, \
200 29,28,27,26,25,24,23,22,21,20, \
201 19,18,17,16,15,14,13,12,11,10, \
202 9,8,7,6,5,4,3,2,1,0)
203// clang-format on
204
205#define hi_parans ()
206
207#define hi_expand1(...) __VA_ARGS__
208#define hi_expand2(...) hi_expand1(hi_expand1(hi_expand1(hi_expand1(__VA_ARGS__))))
209#define hi_expand3(...) hi_expand2(hi_expand2(hi_expand2(hi_expand2(__VA_ARGS__))))
210#define hi_expand4(...) hi_expand3(hi_expand3(hi_expand3(hi_expand3(__VA_ARGS__))))
211
216#define hi_expand(...) hi_expand4(hi_expand4(hi_expand4(hi_expand4(__VA_ARGS__))))
217
218#define hi_for_each_again() hi_for_each_helper
219#define hi_for_each_helper(macro, first_arg, ...) macro(first_arg) __VA_OPT__(hi_for_each_again hi_parans(macro, __VA_ARGS__))
220
226#define hi_for_each(macro, ...) __VA_OPT__(hi_expand(hi_for_each_helper(macro, __VA_ARGS__)))
227
228#ifndef hi_stringify
229#define hi_stringify_(x) #x
230#define hi_stringify(x) hi_stringify_(x)
231#endif
232
233#define hi_cat_(a, b) a##b
234#define hi_cat(a, b) hi_cat_(a, b)
235
236#define hi_return_on_self_assignment(other) \
237 if (&(other) == this) [[unlikely]] \
238 return *this;
239
249#define hi_get_overloaded_macro2(_1, _2, name, ...) name
250
251#define ssizeof(x) (static_cast<ssize_t>(sizeof(x)))
252
267#define hi_assert_abort(...) \
268 do { \
269 ::hi::set_debug_message(__FILE__ ":" hi_stringify(__LINE__) ":" __VA_ARGS__); \
270 hi_assert_break(); \
271 } while (false)
272
283#define hi_check(expression, message, ...) \
284 do { \
285 if (not(expression)) { \
286 if constexpr (__VA_OPT__(not ) false) { \
287 throw parse_error(std::format(message __VA_OPT__(, ) __VA_ARGS__)); \
288 } else { \
289 throw parse_error(message); \
290 } \
291 } \
292 } while (false)
293
305#define hi_check_bounds(x, ...) \
306 do { \
307 if (not ::hi::bound_check(x, __VA_ARGS__)) { \
308 throw parse_error("assert bounds: " hi_stringify(x) " between " hi_for_each(hi_stringify, (__VA_ARGS__))); \
309 } \
310 } while (false)
311
324#define hi_check_subspan(span, offset, ...) \
325 [&](auto _hi_check_subspan_span, size_t _hi_check_subspan_offset, auto... _hi_check_subspan_count) { \
326 if constexpr (sizeof...(_hi_check_subspan_count) == 0) { \
327 if (_hi_check_subspan_offset < _hi_check_subspan_span.size()) { \
328 return _hi_check_subspan_span.subspan(_hi_check_subspan_offset); \
329 } \
330 } else if constexpr (sizeof...(_hi_check_subspan_count) == 1) { \
331 if (_hi_check_subspan_offset + wide_cast<size_t>(_hi_check_subspan_count...) <= _hi_check_subspan_span.size()) { \
332 return _hi_check_subspan_span.subspan(_hi_check_subspan_offset, _hi_check_subspan_count...); \
333 } \
334 } \
335 throw parse_error( \
336 "assert bounds on: " hi_stringify(span) ".subspan(" hi_stringify(offset __VA_OPT__(", ") __VA_ARGS__) ")"); \
337 }(span, offset __VA_OPT__(, ) __VA_ARGS__)
338
349#define hi_check_at(span, index) \
350 [&](auto _hi_check_subspan_span, size_t _hi_check_subspan_index) { \
351 if (_hi_check_subspan_index < _hi_check_subspan_span.size()) { \
352 return _hi_check_subspan_span[_hi_check_subspan_index]; \
353 } else { \
354 throw parse_error("assert bounds on: " hi_stringify(span) "[" hi_stringify(index) "]"); \
355 } \
356 }(span, index)
357
358#define hi_hresult_check(expression) \
359 ([](HRESULT result) { \
360 if (FAILED(result)) { \
361 throw ::hi::io_error(std::format("Call to '{}' failed with {:08x}", #expression, result)); \
362 } \
363 return result; \
364 }(expression))
365
372#define hi_assert(expression, ...) \
373 do { \
374 if (not(expression)) { \
375 hi_assert_abort("assert: " __VA_ARGS__ " not (" hi_stringify(expression) ")"); \
376 } \
377 } while (false)
378
385#define hi_assert_or_return(x, y) \
386 if (!(x)) { \
387 [[unlikely]] return y; \
388 }
389
398#define hi_assert_bounds(x, ...) \
399 do { \
400 if (not ::hi::bound_check(x, __VA_ARGS__)) { \
401 hi_assert_abort("assert bounds: " hi_stringify(x) " between " hi_for_each(hi_stringify, (__VA_ARGS__))); \
402 } \
403 } while (false)
404
411#define hi_assert_not_null(x, ...) \
412 do { \
413 if (x == nullptr) { \
414 hi_assert_abort("assert not-null: " __VA_ARGS__ " (" hi_stringify(x) ")"); \
415 } \
416 } while (false)
417
425#ifndef NDEBUG
426#define hi_axiom(expression, ...) hi_assert(expression __VA_OPT__(, ) __VA_ARGS__)
427#else
428#define hi_axiom(expression, ...) hi_assume(expression)
429#endif
430
440#ifndef NDEBUG
441#define hi_axiom_bounds(x, ...) hi_assert_bounds(x, __VA_ARGS__)
442#else
443#define hi_axiom_bounds(x, ...) hi_assume(not ::hi::bound_check(x, __VA_ARGS__))
444#endif
445
452#ifndef NDEBUG
453#define hi_axiom_not_null(expression, ...) hi_assert_not_null(expression __VA_OPT__(, ) __VA_ARGS__)
454#else
455#define hi_axiom_not_null(expression, ...) hi_assume(expression != nullptr)
456#endif
457
463#ifndef NDEBUG
464#define hi_no_default(...) \
465 [[unlikely]] hi_assert_abort("Reached no-default:" __VA_ARGS__); \
466 std::terminate()
467#else
468#define hi_no_default(...) std::unreachable()
469#endif
470
476#define hi_static_no_default(...) \
477 []<bool Flag = false>() \
478 { \
479 static_assert(Flag, "No default: " __VA_ARGS__); \
480 } \
481 ()
482
488#define hi_not_implemented(...) \
489 [[unlikely]] hi_assert_abort("Not implemented: " __VA_ARGS__); \
490 std::terminate()
491
497#define hi_static_not_implemented(...) hi_static_no_default("Not implemented: " __VA_ARGS__)
498
505#define hi_print(fmt, ...) console_output(std::format(fmt __VA_OPT__(, ) __VA_ARGS__))
506
507#define hi_format_argument_check(arg) \
508 static_assert( \
509 ::std::is_default_constructible_v<std::formatter<std::decay_t<decltype(arg)>>>, \
510 "std::format, argument '" #arg "' does not have a specialized std::formatter<>.");
511
524#define hi_format_check(fmt, ...) \
525 static_assert(::hi::format_count(fmt) != -1, "std::format, Unexpected '{' inside argument-format."); \
526 static_assert(::hi::format_count(fmt) != -2, "std::format, Unexpected '}' without corresponding '{'."); \
527 static_assert(::hi::format_count(fmt) != -3, "std::format, Missing '}' at end of format string."); \
528 static_assert( \
529 ::hi::format_count(fmt) == hi_num_va_args(__VA_ARGS__), "std::format, invalid number of arguments for format string."); \
530 hi_for_each(hi_format_argument_check, __VA_ARGS__)
531
532#define hi_log(level, fmt, ...) \
533 hi_format_check(fmt __VA_OPT__(, ) __VA_ARGS__); \
534 ::hi::log_global.add<level, __FILE__, __LINE__, fmt>(__VA_ARGS__)
535
536#define hi_log_debug(fmt, ...) hi_log(::hi::global_state_type::log_debug, fmt __VA_OPT__(, ) __VA_ARGS__)
537#define hi_log_info(fmt, ...) hi_log(::hi::global_state_type::log_info, fmt __VA_OPT__(, ) __VA_ARGS__)
538#define hi_log_statistics(fmt, ...) hi_log(::hi::global_state_type::log_statistics, fmt __VA_OPT__(, ) __VA_ARGS__)
539#define hi_log_trace(fmt, ...) hi_log(::hi::global_state_type::log_trace, fmt __VA_OPT__(, ) __VA_ARGS__)
540#define hi_log_audit(fmt, ...) hi_log(::hi::global_state_type::log_audit, fmt __VA_OPT__(, ) __VA_ARGS__)
541#define hi_log_warning(fmt, ...) hi_log(::hi::global_state_type::log_warning, fmt __VA_OPT__(, ) __VA_ARGS__)
542#define hi_log_error(fmt, ...) hi_log(::hi::global_state_type::log_error, fmt __VA_OPT__(, ) __VA_ARGS__)
543#define hi_log_fatal(fmt, ...) \
544 hi_log(::hi::global_state_type::log_fatal, fmt __VA_OPT__(, ) __VA_ARGS__); \
545 hi_assert_abort(); \
546 std::terminate()
547
548#define hi_log_info_once(name, fmt, ...) \
549 do { \
550 if (++::hi::global_counter<name> == 1) { \
551 hi_log(::hi::global_state_type::log_info, fmt __VA_OPT__(, ) __VA_ARGS__); \
552 } \
553 } while (false)
554
555#define hi_log_error_once(name, fmt, ...) \
556 do { \
557 if (++::hi::global_counter<name> == 1) { \
558 hi_log(::hi::global_state_type::log_error, fmt __VA_OPT__(, ) __VA_ARGS__); \
559 } \
560 } while (false)
561
572#define hi_num_valid_arguments(ATTRIBUTES, NAME, CHECK_CALLABLE) \
573 template<size_t I, typename First, typename... Rest> \
574 ATTRIBUTES size_t _ ## NAME() \
575 { \
576 if constexpr (I > 1) { \
577 return _ ## NAME<I - 1, Rest..., First>(); \
578 } else if constexpr (I == 1) { \
579 return _ ## NAME<0, Rest...>(); \
580 } else if constexpr (requires (First &&f, Rest &&...r) { CHECK_CALLABLE(std::forward<First>(f), std::forward<Rest>(r)...); }) { \
581 return sizeof...(Rest) + 1; \
582 } else if constexpr (sizeof...(Rest)) { \
583 return _ ## NAME<sizeof...(Rest) + 1, First, Rest...>(); \
584 } else { \
585 return 0; \
586 } \
587 } \
588 template<typename... Args> \
589 ATTRIBUTES size_t NAME() \
590 { \
591 if constexpr (sizeof...(Args) != 0) { \
592 return _ ## NAME<0, Args...>(); \
593 } else { \
594 return 0; \
595 } \
596 }
597
608// To use the right arguments, we first need to rotate them before we can
609// remove the ones we don't need. Since we can only remove arguments on the left
610// side.
611#define hi_call_left_arguments(ATTRIBUTES, NAME, CALLABLE) \
612 template<size_t N, size_t R, typename First, typename... Rest>\
613 ATTRIBUTES auto _ ## NAME(First &&first, Rest &&...rest)\
614 {\
615 if constexpr (R != 0) { \
616 return _ ## NAME<N, R - 1, Rest..., First>(std::forward<Rest>(rest)..., std::forward<First>(first)); \
617 } else if constexpr (N != sizeof...(Rest) + 1) {\
618 return _ ## NAME<N, 0, Rest...>(std::forward<Rest>(rest)...);\
619 } else { \
620 return CALLABLE(std::forward<First>(first), std::forward<Rest>(rest)...);\
621 }\
622 }\
623 template<size_t N, typename... Args>\
624 ATTRIBUTES auto NAME(Args &&...args)\
625 {\
626 static_assert(N <= sizeof...(Args)); \
627 if constexpr (N == sizeof...(Args)) { \
628 return CALLABLE(std::forward<Args>(args)...); \
629 } else {\
630 return _ ## NAME<N, N, Args...>(std::forward<Args>(args)...); \
631 }\
632 }
633
644#define hi_call_right_arguments(ATTRIBUTES, NAME, CALLABLE) \
645 template<size_t Skip>\
646 ATTRIBUTES auto NAME()\
647 {\
648 static_assert(Skip == 0); \
649 return CALLABLE();\
650 }\
651 template<size_t Skip, typename First, typename... Rest>\
652 ATTRIBUTES auto NAME(First &&first, Rest &&...rest)\
653 {\
654 static_assert(Skip <= (sizeof...(Rest) + 1)); \
655 if constexpr (Skip != 0) {\
656 return NAME<Skip - 1, Rest...>(std::forward<Rest>(rest)...);\
657 } else {\
658 return CALLABLE(std::forward<First>(first), std::forward<Rest>(rest)...);\
659 }\
660 }
661