10#if !defined(__cpp_lib_format)
11#error "This library requires std::format"
13#include "source_location.h"
39#define ASSUMING_DEBUG 0
41#define ASSUMING_DEBUG 1
46#ifndef ASSUMING_INCLUDE_MAGIC_ENUM
47#if __has_include(<magic_enum.hpp>)
48#define ASSUMING_INCLUDE_MAGIC_ENUM 1
50#define ASSUMING_INCLUDE_MAGIC_ENUM 0
55#include "source_location.h"
60#if ASSUMING_INCLUDE_MAGIC_ENUM
61#include <magic_enum.hpp>
64#ifdef __has_cpp_attribute
65#if __has_cpp_attribute(assume)
67#define GHPL_CPP23_ASSUME(...) [[assume(__VA_ARGS__)]];
70#ifndef GHPL_CPP23_ASSUME
71#define GHPL_CPP23_ASSUME(...)
76#define ASSUMING_ASSUME(cond) GHPL_CPP23_ASSUME(cond) (__assume(cond), (::std::ignore = (cond)))
77#elif defined(__GNUC__)
78#define ASSUMING_ASSUME(cond) GHPL_CPP23_ASSUME(cond) ((cond) ? static_cast<void>(0) : __builtin_unreachable())
80#define ASSUMING_ASSUME(cond) GHPL_CPP23_ASSUME(cond) static_cast<void>((cond) ? 0 : 0)
84#if ASSUMING_DEBUG || defined(DOXYGEN)
87#define Assuming(exp, ...) { if (auto&& _assuming_exp_v = (exp); !_assuming_exp_v) [[unlikely]] \
88 ::ghassanpl::ReportAssumptionFailure(#exp " will evalute to true", { { #exp, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_exp_v)) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
91#define AssumingNotReachable(...) { ::ghassanpl::ReportAssumptionFailure("execution will never reach this point", {}, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
94#define AssumingNotRecursive(...) \
95 static int _assuming_recursion_counter##__LINE__ = 0; \
96 if (_assuming_recursion_counter##__LINE__ != 0) \
97 ::ghassanpl::ReportAssumptionFailure("enclosing block will not be entered recursively", {}, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); \
98 const ::ghassanpl::detail::RecursionScopeMarker _assuming_scope_marker##__LINE__( _assuming_recursion_counter##__LINE__ )
101#define AssumingSingleThread(...) { \
102 static std::thread::id _assuming_thread_id = std::this_thread::get_id(); \
103 auto _assuming_current_thread_id = std::this_thread::get_id(); \
104 if (_assuming_thread_id != _assuming_current_thread_id)\
105 ::ghassanpl::ReportAssumptionFailure("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__)); \
109#define AssumingOnThread(thread_to_check, ...) { \
110 auto _assuming_thread_id = (thread_to_check); \
111 auto _assuming_current_thread_id = std::this_thread::get_id(); \
112 if (_assuming_thread_id != _assuming_current_thread_id)\
113 ::ghassanpl::ReportAssumptionFailure("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__)); \
117#define AssumingNotOnThread(thread_to_check, ...) { \
118 auto _assuming_thread_id = (thread_to_check); \
119 auto _assuming_current_thread_id = std::this_thread::get_id(); \
120 if (_assuming_thread_id == _assuming_current_thread_id)\
121 ::ghassanpl::ReportAssumptionFailure("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__)); \
125#define AssumingNull(exp, ...) { if (auto _assuming_exp_v = (const void*)std::to_address(exp); _assuming_exp_v != nullptr) [[unlikely]] \
126 ::ghassanpl::ReportAssumptionFailure(#exp " will be null", { { #exp, std::format("{}", _assuming_exp_v) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
129#define AssumingNotNull(exp, ...) { if (auto _assuming_exp_v = (const void*)std::to_address(exp); _assuming_exp_v == nullptr) [[unlikely]] \
130 ::ghassanpl::ReportAssumptionFailure(#exp " will not be null", { { #exp, std::format("{}", _assuming_exp_v) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
133#define AssumingBinOp(a, b, op, text, ...) { auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); if (!(_assuming_a_v op _assuming_b_v)) [[unlikely]] \
134 ::ghassanpl::ReportAssumptionFailure(#a " will " text " " #b, { \
135 { #a, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_a_v)) }, \
136 { #b, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_b_v)) } \
137 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
140#define AssumingEqual(a, b, ...) AssumingBinOp(a, b, ==, "be equal to", __VA_ARGS__)
142#define AssumingNotEqual(a, b, ...) AssumingBinOp(a, b, !=, "not be equal to", __VA_ARGS__)
144#define AssumingGreater(a, b, ...) AssumingBinOp(a, b, >, "be greater than", __VA_ARGS__)
146#define AssumingLess(a, b, ...) AssumingBinOp(a, b, <, "be less than", __VA_ARGS__)
148#define AssumingGreaterEqual(a, b, ...) AssumingBinOp(a, b, >=, "be greater or equal to", __VA_ARGS__)
150#define AssumingLessEqual(a, b, ...) AssumingBinOp(a, b, <=, "be less or equal to", __VA_ARGS__)
153#define AssumingContainsBits(a, b, ...) { auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); if (!((_assuming_a_v & _assuming_b_v) == _assuming_b_v)) [[unlikely]] \
154 ::ghassanpl::ReportAssumptionFailure(#a " will contain flags " #b, { \
155 { #a, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_a_v)) }, \
156 { #b, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_b_v)) } \
157 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
160#define AssumingZero(a, ...) AssumingBinOp(a, 0, ==, "be equal to", __VA_ARGS__)
163#define AssumingEmpty(exp, ...) { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); !empty(_assuming_exp_v)) [[unlikely]] \
164 ::ghassanpl::ReportAssumptionFailure(#exp " will be empty", { { "size of " #exp, std::format("{}", size(_assuming_exp_v)) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
167#define AssumingNotEmpty(exp, ...) { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); empty(_assuming_exp_v)) [[unlikely]] \
168 ::ghassanpl::ReportAssumptionFailure(#exp " will not be empty", { { "size of " #exp, std::format("{}", size(_assuming_exp_v)) } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
171#define AssumingNullOrEmpty(exp, ...) { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); !::ghassanpl::detail::IsNullOrEmpty(_assuming_exp_v)) [[unlikely]] \
172 ::ghassanpl::ReportAssumptionFailure(#exp " will be null or empty", { { #exp, _assuming_exp_v ? std::format("'{}'", _assuming_exp_v) : "(null)" } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
175#define AssumingNotNullOrEmpty(exp, ...) { using std::empty; using std::size; if (auto&& _assuming_exp_v = (exp); ::ghassanpl::detail::IsNullOrEmpty(_assuming_exp_v)) [[unlikely]] \
176 ::ghassanpl::ReportAssumptionFailure(#exp " will not be null or empty", { { #exp, _assuming_exp_v ? std::format("'{}'", _assuming_exp_v) : "(null)" } }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
179#define AssumingValidIndex(_index, _container, ...) { using std::size; auto&& _assuming_index = (_index); auto&& _assuming_container = (_container); const auto _assuming_container_size = size(_assuming_container); \
180 if (!(_assuming_index >= 0 && size_t(_assuming_index) < _assuming_container_size)) [[unlikely]] { \
181 ::ghassanpl::ReportAssumptionFailure(#_index " will be a valid index to " #_container, { \
182 { #_index, std::format("{}", _assuming_index) }, \
183 { "size of " #_container, std::format("{}", _assuming_container_size) }, \
184 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } }
187#define AssumingValidIterator(_iterator, _container, ...) { using std::end; auto&& _assuming_iterator = (_iterator); auto&& _assuming_container = (_container); const auto _assuming_end = end(_assuming_container); \
188 if (_assuming_iterator == _assuming_end) [[unlikely]] { \
189 ::ghassanpl::ReportAssumptionFailure(#_iterator " will be a valid iterator to " #_container, {}, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); } }
192#define AssumingBetween(v, a, b, ...) { 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]] \
193 ::ghassanpl::ReportAssumptionFailure(#v " will be between " #a " and " #b, { \
194 { #v, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_v_v)) }, \
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__)); }
200#define AssumingBetweenInclusive(v, a, b, ...) { 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]] \
201 ::ghassanpl::ReportAssumptionFailure(#v " will be between " #a " and " #b " (inclusive)", { \
202 { #v, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_v_v)) }, \
203 { #a, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_a_v)) }, \
204 { #b, std::format("{}", ::ghassanpl::detail::GetFormattable(_assuming_b_v)) } \
205 }, ::ghassanpl::detail::AdditionalDataToString(__VA_ARGS__)); }
208#define Assuming(exp, ...) ASSUMING_ASSUME(!!(exp))
209#define AssumingNotReachable(...) ASSUMING_ASSUME(false)
212#define AssumingNotRecursive(...) \
213 static int _assuming_recursion_counter##__LINE__ = 0; \
214 ASSUMING_ASSUME(_assuming_recursion_counter##__LINE__ == 0); \
215 const ::ghassanpl::detail::RecursionScopeMarker _assuming_scope_marker##__LINE__( _assuming_recursion_counter##__LINE__ )
216#define AssumingSingleThread(...) { \
217 static std::thread::id _assuming_thread_id = std::this_thread::get_id(); \
218 auto _assuming_current_thread_id = std::this_thread::get_id(); \
219 ASSUMING_ASSUME(_assuming_thread_id == _assuming_current_thread_id); \
221#define AssumingOnThread(thread_to_check, ...) { \
222 auto _assuming_thread_id = (thread_to_check); \
223 auto _assuming_current_thread_id = std::this_thread::get_id(); \
224 ASSUMING_ASSUME(_assuming_thread_id == _assuming_current_thread_id); \
228#define AssumingNull(exp, ...) ASSUMING_ASSUME(!((exp) != nullptr))
229#define AssumingNotNull(exp, ...) ASSUMING_ASSUME(!((exp) == nullptr))
230#define AssumingBinOp(a, b, op, text, ...) ASSUMING_ASSUME(((a) op (b)))
231#define AssumingEqual(a, b, ...) AssumingBinOp(a, b, ==, "be equal to", __VA_ARGS__)
232#define AssumingZero(a, ...) AssumingBinOp(a, 0, ==, "be equal to", __VA_ARGS__)
233#define AssumingNotEqual(a, b, ...) AssumingBinOp(a, b, !=, "not be equal to", __VA_ARGS__)
234#define AssumingGreater(a, b, ...) AssumingBinOp(a, b, >, "be greater than", __VA_ARGS__)
235#define AssumingLess(a, b, ...) AssumingBinOp(a, b, <, "be less than", __VA_ARGS__)
236#define AssumingGreaterEqual(a, b, ...) AssumingBinOp(a, b, >=, "be greater or equal to", __VA_ARGS__)
237#define AssumingLessEqual(a, b, ...) AssumingBinOp(a, b, <=, "be less or equal to", __VA_ARGS__)
238#define AssumingEmpty(exp, ...) { using std::empty; ASSUMING_ASSUME(empty(exp)); }
239#define AssumingNotEmpty(exp, ...) { using std::empty; using std::size; ASSUMING_ASSUME(!empty(exp)); }
240#define AssumingNullOrEmpty(exp, ...) { using std::empty; using std::size; ASSUMING_ASSUME(::ghassanpl::detail::IsNullOrEmpty(exp)); }
241#define AssumingNotNullOrEmpty(exp, ...) { using std::empty; using std::size; ASSUMING_ASSUME(!::ghassanpl::detail::IsNullOrEmpty(exp)); }
242#define AssumingValidIndex(_index, _container, ...) { using std::size; auto&& _assuming_index = (_index); ASSUMING_ASSUME(((_assuming_index) >= 0 && size_t(_assuming_index) < size(_container))); }
243#define AssumingValidIterator(_iterator, _container, ...) { using std::end; auto&& _assuming_iterator = (_iterator); auto&& _assuming_container = (_container); const auto _assuming_end = end(_assuming_container); ASSUMING_ASSUME(!(_assuming_iterator == _assuming_end)); }
244#define AssumingBetween(v, a, b, ...) { auto&& _assuming_v_v = (v); auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); ASSUMING_ASSUME(_assuming_v_v >= _assuming_a_v && _assuming_v_v < _assuming_b_v); }
245#define AssumingBetweenInclusive(v, a, b, ...) { auto&& _assuming_v_v = (v); auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); ASSUMING_ASSUME(_assuming_v_v >= _assuming_a_v && _assuming_v_v <= _assuming_b_v); }
247#define AssumingContainsBits(a, b, ...) { auto&& _assuming_a_v = (a); auto&& _assuming_b_v = (b); ASSUMING_ASSUME(!((_assuming_a_v & _assuming_b_v) == _assuming_b_v)); }
258 inline bool IsNullOrEmpty(
const char* str) {
return str ==
nullptr || str[0] == 0; }
259 template <
typename T>
260 inline bool IsNullOrEmpty(T&& str) {
using std::empty;
return empty(str); }
268 template <
typename T>
269 concept formattable =
requires { { std::formatter<T>{} }; };
271 template <
typename T>
274 template <
typename T>
278#if ASSUMING_INCLUDE_MAGIC_ENUM
279 if constexpr (std::is_enum_v<simple_type>)
280 return magic_enum::enum_name(
val);
283 if constexpr (std::is_constructible_v<std::string_view, simple_type>)
284 return std::forward<T>(
val);
285 else if constexpr (std::is_pointer_v<simple_type>)
286 return (
const void*)std::to_address(std::forward<T>(
val));
289 std::stringstream
ss;
291 return std::move(
ss).str();
294 return std::format(
"<{}>",
typeid(
simple_type).name());
296 return std::forward<T>(
val);
300 template <
typename T,
typename...
ARGS>
302 return std::vformat(std::forward<T>(
fmt), std::make_format_args(
GetFormattable(std::forward<ARGS>(
args))...));
308 explicit RecursionScopeMarker(
int& counter) : mCounter(counter) { ++mCounter; }
309 ~RecursionScopeMarker() { --mCounter; }
317 inline void DefaultReportAssumptionFailure(std::string_view
expectation, std::initializer_list<std::pair<std::string_view, std::string>> values, std::string data, source_location
loc)
319 throw std::make_tuple(
335#if defined(ASSUMING_REPORT_NORETURN) && ASSUMING_REPORT_NORETURN
338 inline void ReportAssumptionFailure(std::string_view
expectation, std::initializer_list<std::pair<std::string_view, std::string>> values, std::string data, source_location
loc
342 = source_location::current()
349 DefaultReportAssumptionFailure(std::move(
expectation), std::move(values), std::move(data), std::move(
loc));
void(* AssumptionFailureHandler)(std::string_view expectation, std::initializer_list< std::pair< std::string_view, std::string > > values, std::string data, source_location loc)
This function must be provided by your own code, as it is called by an assumption macro with a failin...
constexpr auto bit_count
Equal to the number of bits in the type.
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.