10#if !defined(__cpp_lib_format)
11#error "This library requires std::format"
14#include "source_location.h"
40#define ASSUMING_DEBUG 0
42#define ASSUMING_DEBUG 1
47#ifndef ASSUMING_INCLUDE_MAGIC_ENUM
48#if __has_include(<magic_enum_format.hpp>)
49#define ASSUMING_INCLUDE_MAGIC_ENUM 1
51#define ASSUMING_INCLUDE_MAGIC_ENUM 0
56#ifndef ASSUMING_BREAKPOINT
57#if __has_include(<debugging>)
59#define ASSUMING_BREAKPOINT std::breakpoint()
62#define __has_builtin(x) 0
64#if __has_builtin(__builtin_debugtrap)
65#define ASSUMING_BREAKPOINT __builtin_debugtrap()
66#elif (defined(__clang__) || defined(__GNUC__)) && !defined(_MSC_VER)
67#define ASSUMING_BREAKPOINT __builtin_trap()
68#elif defined(_MSC_VER)
69#define ASSUMING_BREAKPOINT __debugbreak()
71#define ASSUMING_BREAKPOINT do { try { throw 0; } catch (...) {} } while (false)
77#ifndef ASSUMING_USE_STACKTRACE
78#if defined(__cpp_lib_stacktrace)
79#define ASSUMING_USE_STACKTRACE 1
81#define ASSUMING_USE_STACKTRACE 0
92#if ASSUMING_USE_STACKTRACE
93#if defined(__cpp_lib_stacktrace)
96#undef ASSUMING_USE_STACKTRACE
97#define ASSUMING_USE_STACKTRACE 0
101#if ASSUMING_INCLUDE_MAGIC_ENUM
102#include <magic_enum/magic_enum_format.hpp>
105#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
107#define GHPL_UNREACHABLE() std::unreachable()
109#if (defined(__clang__) || defined(__GNUC__)) && !defined(_MSC_VER)
110#define GHPL_UNREACHABLE() (__builtin_unreachable())
112#define GHPL_UNREACHABLE() GHPL_ASSUME(false)
116#ifdef __has_cpp_attribute
117#if __has_cpp_attribute(assume)
119#define GHPL_ASSUME(...) [[assume(__VA_ARGS__)]]; (::std::ignore = (__VA_ARGS__))
123#if (defined(__clang__) || defined(__GNUC__)) && !defined(_MSC_VER)
124#define GHPL_ASSUME(...) ((__VA_ARGS__) ? static_cast<void>(0) : GHPL_UNREACHABLE())
126#define GHPL_ASSUME(...) (__assume(__VA_ARGS__), (::std::ignore = (__VA_ARGS__)))
130#if ASSUMING_DEBUG || defined(DOXYGEN)
132#define ASSUMING_HANDLE_HANDLER_RESULT(result) do { \
135 case ::ghassanpl::AssumptionHandlerResult::Continue: break; \
136 case ::ghassanpl::AssumptionHandlerResult::Break: ASSUMING_BREAKPOINT; break; \
137 case ::ghassanpl::AssumptionHandlerResult::Terminate: std::abort(); break; \
141#define ASSUMING_REPORT(...) ASSUMING_HANDLE_HANDLER_RESULT(::ghassanpl::ReportAssumptionFailure(__VA_ARGS__))
147#define Assuming(exp, ...) do { if (auto&& _assuming_exp_v = (exp); !_assuming_exp_v) [[unlikely]] \
148 ASSUMING_REPORT(#exp " will evalute to true", { { #exp, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_exp_v)) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
151#define AssumingNotReachable(...) do { ASSUMING_REPORT("execution will never reach this point", {}, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); GHPL_UNREACHABLE(); } while (false)
154#define AssumingNotRecursive(...) \
155 static int _assuming_recursion_counter##__LINE__ = 0; \
156 if (_assuming_recursion_counter##__LINE__ != 0) \
157 ASSUMING_REPORT("enclosing block will not be entered recursively", {}, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); \
158 const ::ghassanpl::detail::RecursionScopeMarker _assuming_scope_marker##__LINE__( _assuming_recursion_counter##__LINE__ )
161#define AssumingSingleThread(...) do { \
162 static std::thread::id _assuming_thread_id = std::this_thread::get_id(); \
163 auto _assuming_current_thread_id = std::this_thread::get_id(); \
164 if (_assuming_thread_id != _assuming_current_thread_id)\
165 ASSUMING_REPORT("this code will be executed in one thread only", { {"required_thread_id", std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_thread_id))}, {"thread_id", std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_current_thread_id))} }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); \
169#define AssumingOnThread(thread_to_check, ...) do { \
170 auto _assuming_thread_id = (thread_to_check); \
171 auto _assuming_current_thread_id = std::this_thread::get_id(); \
172 if (_assuming_thread_id != _assuming_current_thread_id)\
173 ASSUMING_REPORT("this code will be executed in one thread only", { {"required_thread_id", std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_thread_id))}, { "thread_id", std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_current_thread_id))} }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); \
177#define AssumingNotOnThread(thread_to_check, ...) do { \
178 auto _assuming_thread_id = (thread_to_check); \
179 auto _assuming_current_thread_id = std::this_thread::get_id(); \
180 if (_assuming_thread_id == _assuming_current_thread_id)\
181 ASSUMING_REPORT("this code will not be executed in specific thread", { {"forbidden_thread_id", std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_thread_id))}, { "thread_id", std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_current_thread_id))} }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); \
185#define AssumingNull(exp, ...) do { if (auto _assuming_exp_v = (const void*)std::to_address(exp); _assuming_exp_v != nullptr) [[unlikely]] \
186 ASSUMING_REPORT(#exp " will be null", { { #exp, std::format("{}", _assuming_exp_v) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
189#define AssumingNotNull(exp, ...) do { if (auto _assuming_exp_v = (const void*)std::to_address(exp); _assuming_exp_v == nullptr) [[unlikely]] \
190 ASSUMING_REPORT(#exp " will not be null", { { #exp, std::format("{}", _assuming_exp_v) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
193#define AssumingBinOp(a, b, op, text, ...) do { auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); if (!(_assuming_a_v op _assuming_b_v)) [[unlikely]] \
194 ASSUMING_REPORT(#a " will " text " " #b, { \
195 { #a, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_a_v)) }, \
196 { #b, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_b_v)) } \
197 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
200#define AssumingEqual(a, b, ...) AssumingBinOp(a, b, ==, "be equal to", __VA_ARGS__)
202#define AssumingNotEqual(a, b, ...) AssumingBinOp(a, b, !=, "not be equal to", __VA_ARGS__)
204#define AssumingGreater(a, b, ...) AssumingBinOp(a, b, >, "be greater than", __VA_ARGS__)
206#define AssumingLess(a, b, ...) AssumingBinOp(a, b, <, "be less than", __VA_ARGS__)
208#define AssumingGreaterEqual(a, b, ...) AssumingBinOp(a, b, >=, "be greater or equal to", __VA_ARGS__)
210#define AssumingLessEqual(a, b, ...) AssumingBinOp(a, b, <=, "be less or equal to", __VA_ARGS__)
213#define AssumingContainsBits(a, b, ...) do { auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); if (!((_assuming_a_v & _assuming_b_v) == _assuming_b_v)) [[unlikely]] \
214 ASSUMING_REPORT(#a " will contain flags " #b, { \
215 { #a, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_a_v)) }, \
216 { #b, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_b_v)) } \
217 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
220#define AssumingZero(a, ...) AssumingBinOp(a, 0, ==, "be equal to", __VA_ARGS__)
223#define AssumingEmpty(exp, ...) do { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); !empty(_assuming_exp_v)) [[unlikely]] \
224 ASSUMING_REPORT(#exp " will be empty", { { "size of " #exp, std::format("{}", size(_assuming_exp_v)) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
227#define AssumingNotEmpty(exp, ...) do { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); empty(_assuming_exp_v)) [[unlikely]] \
228 ASSUMING_REPORT(#exp " will not be empty", { { "size of " #exp, std::format("{}", size(_assuming_exp_v)) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
231#define AssumingNullOrEmpty(exp, ...) do { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); !::ghassanpl::detail::IsNullOrEmpty(_assuming_exp_v)) [[unlikely]] \
232 ASSUMING_REPORT(#exp " will be null or empty", { { #exp, _assuming_exp_v ? std::format("'{}'", _assuming_exp_v) : "(null)" } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
235#define AssumingNotNullOrEmpty(exp, ...) do { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); ::ghassanpl::detail::IsNullOrEmpty(_assuming_exp_v)) [[unlikely]] \
236 ASSUMING_REPORT(#exp " will not be null or empty", { { #exp, _assuming_exp_v ? std::format("'{}'", _assuming_exp_v) : "(null)" } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
239#define AssumingValidIndex(_index, _container, ...) do { using std::size; auto&& _assuming_index = (_index); auto&& _assuming_container = (_container); const auto _assuming_container_size = size(_assuming_container); \
240 if (!(_assuming_index >= 0 && size_t(_assuming_index) < _assuming_container_size)) [[unlikely]] { \
241 ASSUMING_REPORT(#_index " will be a valid index to " #_container, { \
242 { #_index, std::format("{}", _assuming_index) }, \
243 { "size of " #_container, std::format("{}", _assuming_container_size) }, \
244 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } } while (false)
247#define AssumingContains(_key, _container, ...) do { auto&& _assuming_key = (_key); auto&& _assuming_container = (_container); \
248 if (!(_assuming_container.contains(_assuming_key))) [[unlikely]] { \
249 ASSUMING_REPORT(#_key " will be a valid key to " #_container, { \
250 { #_key, std::format("{}", _assuming_key) }, \
251 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } } while (false)
254#define AssumingValidIterator(_iterator, _container, ...) do { using std::end; auto&& _assuming_iterator = (_iterator); auto&& _assuming_container = (_container); const auto _assuming_end = end(_assuming_container); \
255 if (_assuming_iterator == _assuming_end) [[unlikely]] { \
256 ASSUMING_REPORT(#_iterator " will be a valid iterator to " #_container, {}, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } } while (false)
259#define AssumingBetween(v, a, b, ...) do { auto&& _assuming_v_v = (v); auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); if (!(_assuming_v_v >= _assuming_a_v && _assuming_v_v < _assuming_b_v)) [[unlikely]] \
260 ASSUMING_REPORT(#v " will be between " #a " and " #b, { \
261 { #v, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_v_v)) }, \
262 { #a, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_a_v)) }, \
263 { #b, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_b_v)) } \
264 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
267#define AssumingBetweenInclusive(v, a, b, ...) do { auto&& _assuming_v_v = (v); auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); if (!(_assuming_v_v >= _assuming_a_v && _assuming_v_v <= _assuming_b_v)) [[unlikely]] \
268 ASSUMING_REPORT(#v " will be between " #a " and " #b " (inclusive)", { \
269 { #v, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_v_v)) }, \
270 { #a, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_a_v)) }, \
271 { #b, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_b_v)) } \
272 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } while (false)
275#define Assuming(exp, ...) GHPL_ASSUME(!!(exp))
276#define AssumingNotReachable(...) GHPL_ASSUME(false)
279#define AssumingNotRecursive(...) do {} while (false)
280#define AssumingSingleThread(...) do {} while (false)
281#define AssumingOnThread(thread_to_check, ...) do { \
282 std::ignore = (thread_to_check); \
285#define AssumingNull(exp, ...) GHPL_ASSUME(!((const void*)std::to_address(exp) != nullptr))
286#define AssumingNotNull(exp, ...) GHPL_ASSUME(!((const void*)std::to_address(exp) == nullptr))
287#define AssumingBinOp(a, b, op, text, ...) GHPL_ASSUME(((a) op (b)))
288#define AssumingEqual(a, b, ...) AssumingBinOp(a, b, ==, "be equal to", __VA_ARGS__)
289#define AssumingZero(a, ...) AssumingBinOp(a, 0, ==, "be equal to", __VA_ARGS__)
290#define AssumingNotEqual(a, b, ...) AssumingBinOp(a, b, !=, "not be equal to", __VA_ARGS__)
291#define AssumingGreater(a, b, ...) AssumingBinOp(a, b, >, "be greater than", __VA_ARGS__)
292#define AssumingLess(a, b, ...) AssumingBinOp(a, b, <, "be less than", __VA_ARGS__)
293#define AssumingGreaterEqual(a, b, ...) AssumingBinOp(a, b, >=, "be greater or equal to", __VA_ARGS__)
294#define AssumingLessEqual(a, b, ...) AssumingBinOp(a, b, <=, "be less or equal to", __VA_ARGS__)
295#define AssumingEmpty(exp, ...) do { using std::empty; GHPL_ASSUME(empty(exp)); } while (false)
296#define AssumingNotEmpty(exp, ...) do { using std::empty; using std::size; GHPL_ASSUME(!empty(exp)); } while (false)
297#define AssumingNullOrEmpty(exp, ...) do { using std::empty; using std::size; GHPL_ASSUME(::ghassanpl::detail::IsNullOrEmpty(exp)); } while (false)
298#define AssumingNotNullOrEmpty(exp, ...) do { using std::empty; using std::size; GHPL_ASSUME(!::ghassanpl::detail::IsNullOrEmpty(exp)); } while (false)
299#define AssumingValidIndex(_index, _container, ...) do { using std::size; auto&& _assuming_index = (_index); GHPL_ASSUME(((_assuming_index) >= 0 && size_t(_assuming_index) < size(_container))); } while (false)
300#define AssumingValidIterator(_iterator, _container, ...) do { using std::end; auto&& _assuming_iterator = (_iterator); auto&& _assuming_container = (_container); const auto _assuming_end = end(_assuming_container); GHPL_ASSUME(!(_assuming_iterator == _assuming_end)); } while (false)
301#define AssumingBetween(v, a, b, ...) do { auto&& _assuming_v_v = (v); auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); GHPL_ASSUME(_assuming_v_v >= _assuming_a_v && _assuming_v_v < _assuming_b_v); } while (false)
302#define AssumingBetweenInclusive(v, a, b, ...) do { auto&& _assuming_v_v = (v); auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); GHPL_ASSUME(_assuming_v_v >= _assuming_a_v && _assuming_v_v <= _assuming_b_v); } while (false)
304#define AssumingContainsBits(bits_to_find, bit_container, ...) do { auto&& _assuming_a_v = (bit_container); auto&& _assuming_b_v = (bits_to_find); GHPL_ASSUME(!((_assuming_a_v & _assuming_b_v) == _assuming_b_v)); } while (false)
306#define AssumingContains(_key, _container, ...) do { auto&& _assuming_key = (_key); auto&& _assuming_container = (_container); \
307 GHPL_ASSUME(_assuming_container.contains(_assuming_key)); } while (false)
318 inline bool IsNullOrEmpty(
const char* str) {
return str ==
nullptr || str[0] == 0; }
319 template <
typename T>
320 inline bool IsNullOrEmpty(T&& str) {
using std::empty;
return empty(str); }
328 template <
typename T>
329 concept formattable =
requires { { std::formatter<T>{} }; };
331 template <
typename T>
332 concept streamable =
requires (T val, std::stringstream & ss) { { ss << val }; };
334 template <
typename T>
335 decltype(
auto) GetFormattable(T&& val)
337 using simple_type = std::remove_cvref_t<T>;
338 if constexpr (std::is_constructible_v<std::string_view, simple_type>)
339 return std::forward<T>(val);
340 else if constexpr (std::is_pointer_v<simple_type>)
341 return (
const void*)std::to_address(std::forward<T>(val));
342 else if constexpr (streamable<simple_type> && !formattable<simple_type>)
344 std::stringstream ss;
345 ss << std::forward<T>(val);
346 return std::move(ss).str();
348 else if constexpr (!formattable<simple_type> && !streamable<simple_type>)
349 return std::format(
"<{}>",
typeid(simple_type).name());
351 return std::forward<T>(val);
354 inline std::string AdditionalDataToString() {
return {}; }
356 template <
typename T,
typename... ARGS>
357 std::string AdditionalDataToString(T&& fmt, ARGS&&... args)
359 auto formattable = std::tuple{ GetFormattable(std::forward<ARGS>(args))... };
360 auto format_args = std::apply([](
auto&... args) {
return std::make_format_args(args...); }, formattable);
361 return std::vformat(std::forward<T>(fmt), std::move(format_args));
365 struct RecursionScopeMarker
367 explicit RecursionScopeMarker(
int& counter) : mCounter(counter) { ++mCounter; }
368 ~RecursionScopeMarker() { --mCounter; }
384 inline auto DefaultReportAssumptionFailure(std::string_view expectation, std::initializer_list<std::pair<std::string_view, std::string>> values, std::string data, source_location loc
386 , std::stacktrace stacktrace
390 throw std::make_tuple(
391 std::move(expectation),
396 , std::move(stacktrace)
408 inline auto (*AssumptionFailureHandler)(std::string_view expectation, std::initializer_list<std::pair<std::string_view, std::string>> values, std::string data, source_location loc
409#if ASSUMING_USE_STACKTRACE
410 , std::stacktrace stacktrace
416#if defined(ASSUMING_REPORT_NORETURN) && ASSUMING_REPORT_NORETURN
419 inline auto ReportAssumptionFailure(std::string_view expectation, std::initializer_list<std::pair<std::string_view, std::string>> values, std::string data, source_location loc
423 = source_location::current()
425#if ASSUMING_USE_STACKTRACE
426 , std::stacktrace stacktrace = std::stacktrace::current()
433 , std::move(stacktrace)
438 return DefaultReportAssumptionFailure(std::move(expectation), std::move(values), std::move(data), std::move(loc)
440 , std::move(stacktrace)
auto(* AssumptionFailureHandler)(std::string_view expectation, std::initializer_list< std::pair< std::string_view, std::string > > values, std::string data, source_location loc) -> AssumptionHandlerResult=nullptr
This variable should be set to a handler function by your own code, as it is called by an assumption ...
#define ASSUMING_USE_STACKTRACE
You can define this macro to 1 or 0 to include <stacktrace> (if available) or not; if this functional...
The below code is based on Sun's libm library code, which is licensed under the following license:
Primary namespace for everything in this library.
AssumptionHandlerResult
Return one of these values from the assumption failure handler AssumptionFailureHandler to trigger a ...
@ Terminate
Terminate the program after reporting the assumption failure.
@ Continue
Continue execution after reporting the assumption failure.
@ Break
Break into debugger after reporting the assumption failure.