diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b17cb..17c7d2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,9 +44,28 @@ target_include_directories(dropshell PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/utils ${CMAKE_CURRENT_SOURCE_DIR}/src/contrib - ${CMAKE_CURRENT_SOURCE_DIR}/src/contrib/libassert + $ ) +include(FetchContent) +FetchContent_Declare( + libassert + GIT_REPOSITORY https://github.com/jeremy-rifkin/libassert.git + GIT_TAG v2.1.5 # +) +FetchContent_MakeAvailable(libassert) +target_link_libraries(dropshell libassert::assert) + +# On windows copy libassert.dll to the same directory as the executable for your_target +if(WIN32) + add_custom_command( + TARGET dropshell POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + ) +endif() + # Link libraries target_link_libraries(dropshell PRIVATE ) diff --git a/src/autocomplete.cpp b/src/autocomplete.cpp index 985dbc1..1591b81 100644 --- a/src/autocomplete.cpp +++ b/src/autocomplete.cpp @@ -4,8 +4,8 @@ #include "templates.hpp" #include "services.hpp" #include "servers.hpp" -#include "utils/assert.hpp" +#include #include #include diff --git a/src/contrib/libassert/assert-catch2.hpp b/src/contrib/libassert/assert-catch2.hpp deleted file mode 100644 index 80a7143..0000000 --- a/src/contrib/libassert/assert-catch2.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef LIBASSERT_CATCH2_HPP -#define LIBASSERT_CATCH2_HPP - -#define LIBASSERT_PREFIX_ASSERTIONS -#include - -#include -#include - -#if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL != 0 - #error "Libassert integration does not work with MSVC's non-conformant preprocessor. /Zc:preprocessor must be used." -#endif -// TODO: CHECK/REQUIRE? -#define ASSERT(...) do { try { LIBASSERT_ASSERT(__VA_ARGS__); SUCCEED(); } catch(std::exception& e) { FAIL(e.what()); } } while(false) - -namespace libassert::detail { - // catch line wrapping can't handle ansi sequences before 3.6 https://github.com/catchorg/Catch2/issues/2833 - inline constexpr bool use_color = CATCH_VERSION_MAJOR > 3 || (CATCH_VERSION_MAJOR == 3 && CATCH_VERSION_MINOR >= 6); - - inline void catch2_failure_handler(const assertion_info& info) { - if(use_color) { - enable_virtual_terminal_processing_if_needed(); - } - auto scheme = use_color ? color_scheme::ansi_rgb : color_scheme::blank; - std::string message = std::string(info.action()) + " at " + info.location() + ":"; - if(info.message) { - message += " " + *info.message; - } - message += "\n"; - message += info.statement(scheme) - + info.print_binary_diagnostics(CATCH_CONFIG_CONSOLE_WIDTH, scheme) - + info.print_extra_diagnostics(CATCH_CONFIG_CONSOLE_WIDTH, scheme); - throw std::runtime_error(std::move(message)); - } - - inline auto pre_main = [] () { - set_failure_handler(catch2_failure_handler); - return 1; - } (); -} - -// Some testing utilities - -#define REQUIRE_ASSERT(expr) \ - do { \ - auto handler = ::libassert::get_failure_handler(); \ - ::libassert::set_failure_handler([] (const ::libassert::assertion_info& info) { \ - throw info; \ - }); \ - bool did_assert = false; \ - try { \ - (expr); \ - } catch(const ::libassert::assertion_info& info) { \ - did_assert = true; \ - SUCCEED(); \ - } \ - if(!did_assert) { \ - FAIL("Expected assertion failure from " #expr " however none happened"); \ - } \ - ::libassert::set_failure_handler(handler); \ - } while(false) - -#endif diff --git a/src/contrib/libassert/assert-gtest.hpp b/src/contrib/libassert/assert-gtest.hpp deleted file mode 100644 index d54105e..0000000 --- a/src/contrib/libassert/assert-gtest.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef LIBASSERT_GTEST_HPP -#define LIBASSERT_GTEST_HPP - -#include - -#define LIBASSERT_PREFIX_ASSERTIONS -#include - -#if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL != 0 - #error "Libassert integration does not work with MSVC's non-conformant preprocessor. /Zc:preprocessor must be used." -#endif -#define ASSERT(...) do { try { LIBASSERT_ASSERT(__VA_ARGS__); SUCCEED(); } catch(std::exception& e) { FAIL() << e.what(); } } while(false) -#define EXPECT(...) do { try { LIBASSERT_ASSERT(__VA_ARGS__); SUCCEED(); } catch(std::exception& e) { ADD_FAILURE() << e.what(); } } while(false) - -namespace libassert::detail { - inline void gtest_failure_handler(const assertion_info& info) { - enable_virtual_terminal_processing_if_needed(); // for terminal colors on windows - auto width = terminal_width(stderr_fileno); - const auto& scheme = isatty(stderr_fileno) ? get_color_scheme() : color_scheme::blank; - std::string message = std::string(info.action()) + " at " + info.location() + ":"; - if(info.message) { - message += " " + *info.message; - } - message += "\n"; - message += info.statement() - + info.print_binary_diagnostics(width, scheme) - + info.print_extra_diagnostics(width, scheme); - throw std::runtime_error(std::move(message)); - } - - inline auto pre_main = [] () { - set_failure_handler(gtest_failure_handler); - return 1; - } (); -} - -#endif diff --git a/src/contrib/libassert/assert.hpp b/src/contrib/libassert/assert.hpp deleted file mode 100644 index 3e37b9c..0000000 --- a/src/contrib/libassert/assert.hpp +++ /dev/null @@ -1,997 +0,0 @@ -#ifndef LIBASSERT_HPP -#define LIBASSERT_HPP - -// Copyright (c) 2021-2024 Jeremy Rifkin under the MIT license -// https://github.com/jeremy-rifkin/libassert - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if defined(__has_include) && __has_include() - #include -#else - #include -#endif - -#ifdef __cpp_lib_expected - #include -#endif - -#if LIBASSERT_IS_MSVC - #pragma warning(push) - // warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector and others for - // some reason - // 4275 is the same thing but for base classes - #pragma warning(disable: 4251; disable: 4275) -#endif - -// ===================================================================================================================== -// || Libassert public interface || -// ===================================================================================================================== - -namespace libassert { - // returns the width of the terminal represented by fd, will be 0 on error - [[nodiscard]] LIBASSERT_EXPORT int terminal_width(int fd); - - // Enable virtual terminal processing on windows terminals - LIBASSERT_ATTR_COLD LIBASSERT_EXPORT void enable_virtual_terminal_processing_if_needed(); - - inline constexpr int stdin_fileno = 0; - inline constexpr int stdout_fileno = 1; - inline constexpr int stderr_fileno = 2; - - LIBASSERT_ATTR_COLD LIBASSERT_EXPORT bool isatty(int fd); - - LIBASSERT_ATTR_COLD LIBASSERT_EXPORT bool is_debugger_present() noexcept; - enum class debugger_check_mode { - check_once, - check_every_time, - }; - LIBASSERT_ATTR_COLD LIBASSERT_EXPORT void set_debugger_check_mode(debugger_check_mode mode) noexcept; - - // returns the type name of T - template - [[nodiscard]] std::string_view type_name() noexcept { - return detail::type_name(); - } - - // returns the prettified type name for T - template // TODO: Use this above.... - [[nodiscard]] std::string pretty_type_name() noexcept { - return detail::prettify_type(std::string(detail::type_name())); - } - - // returns a debug stringification of t - template - [[nodiscard]] std::string stringify(const T& t) { - return detail::generate_stringification(t); - } - - // NOTE: string view underlying data should have static storage duration, or otherwise live as long as the scheme - // is in use - struct color_scheme { - std::string_view string; - std::string_view escape; - std::string_view keyword; - std::string_view named_literal; - std::string_view number; - std::string_view punctuation; - std::string_view operator_token; - std::string_view call_identifier; - std::string_view scope_resolution_identifier; - std::string_view identifier; - std::string_view accent; - std::string_view unknown; - std::string_view reset; - LIBASSERT_EXPORT const static color_scheme ansi_basic; - LIBASSERT_EXPORT const static color_scheme ansi_rgb; - LIBASSERT_EXPORT const static color_scheme blank; - }; - - LIBASSERT_EXPORT void set_color_scheme(const color_scheme&); - LIBASSERT_EXPORT const color_scheme& get_color_scheme(); - - // set separator used for diagnostics, by default it is "=>" - // note: not thread-safe - LIBASSERT_EXPORT void set_separator(std::string_view separator); - - std::string highlight(std::string_view expression, const color_scheme& scheme = get_color_scheme()); - - template - [[nodiscard]] std::string highlight_stringify(const T& t, const color_scheme& scheme = get_color_scheme()) { - return highlight(stringify(t), scheme); - } - - // generates a stack trace, formats to the given width - [[nodiscard]] LIBASSERT_ATTR_NOINLINE LIBASSERT_EXPORT - std::string stacktrace(int width = 0, const color_scheme& scheme = get_color_scheme(), std::size_t skip = 0); - - enum class literal_format : unsigned { - // integers and floats are decimal by default, chars are of course chars, and everything else only has one - // format that makes sense - default_format = 0, - integer_hex = 1, - integer_octal = 2, - integer_binary = 4, - integer_character = 8, // format integers as characters and characters as integers - float_hex = 16, - }; - - [[nodiscard]] constexpr literal_format operator|(literal_format a, literal_format b) { - return static_cast( - static_cast::type>(a) | - static_cast::type>(b) - ); - } - - enum class literal_format_mode { - infer, // infer literal formats based on the assertion condition - no_variations, // don't do any literal format variations, just default - fixed_variations // use a fixed set of formats always; note the default format will always be used - }; - - // NOTE: Should not be called during handling of an assertion in the current thread - LIBASSERT_EXPORT void set_literal_format_mode(literal_format_mode); - - // NOTE: Should not be called during handling of an assertion in the current thread - // set a fixed literal format configuration, automatically changes the literal_format_mode; note that the default - // format will always be used along with others - LIBASSERT_EXPORT void set_fixed_literal_format(literal_format); - - enum class path_mode { - // full path is used - full, - // only enough folders needed to disambiguate are provided - disambiguated, // TODO: Maybe just a bad idea...? - // only the file name is used - basename, - }; - LIBASSERT_EXPORT void set_path_mode(path_mode mode); - - enum class assert_type { - debug_assertion, - assertion, - assumption, - panic, - unreachable - }; - - struct assertion_info; - - [[noreturn]] LIBASSERT_EXPORT void default_failure_handler(const assertion_info& info); - - using handler_ptr = void(*)(const assertion_info&); - LIBASSERT_EXPORT handler_ptr get_failure_handler(); - LIBASSERT_EXPORT void set_failure_handler(handler_ptr handler); - - struct LIBASSERT_EXPORT binary_diagnostics_descriptor { - std::string left_expression; - std::string right_expression; - std::string left_stringification; - std::string right_stringification; - bool multiple_formats; - binary_diagnostics_descriptor(); // = default; in the .cpp - binary_diagnostics_descriptor( - std::string_view left_expression, - std::string_view right_expression, - std::string&& left_stringification, - std::string&& right_stringification, - bool multiple_formats - ); - ~binary_diagnostics_descriptor(); // = default; in the .cpp - binary_diagnostics_descriptor(const binary_diagnostics_descriptor&); - binary_diagnostics_descriptor(binary_diagnostics_descriptor&&) noexcept; - binary_diagnostics_descriptor& operator=(const binary_diagnostics_descriptor&); - binary_diagnostics_descriptor& operator=(binary_diagnostics_descriptor&&) noexcept(LIBASSERT_GCC_ISNT_STUPID); - }; - - namespace detail { - struct sv_span { - const std::string_view* data; - std::size_t size; - }; - - // collection of assertion data that can be put in static storage and all passed by a single pointer - struct LIBASSERT_EXPORT assert_static_parameters { - std::string_view macro_name; - assert_type type; - std::string_view expr_str; - source_location location; - sv_span args_strings; - }; - } - - struct extra_diagnostic { - std::string_view expression; - std::string stringification; - }; - - namespace detail { - class path_handler { - public: - virtual ~path_handler() = default; - virtual std::unique_ptr clone() const = 0; - virtual std::string_view resolve_path(std::string_view) = 0; - virtual bool has_add_path() const; - virtual void add_path(std::string_view); - virtual void finalize(); - }; - } - - struct LIBASSERT_EXPORT assertion_info { - std::string_view macro_name; - assert_type type; - std::string_view expression_string; - std::string_view file_name; - std::uint32_t line; - std::string_view function; - std::optional message; - std::optional binary_diagnostics; - std::vector extra_diagnostics; - size_t n_args; - private: - mutable std::variant trace; // lazy, resolved when needed - mutable std::unique_ptr path_handler; - detail::path_handler* get_path_handler() const; // will get and setup the path handler - public: - assertion_info() = delete; - assertion_info( - const detail::assert_static_parameters* static_params, - cpptrace::raw_trace&& raw_trace, - size_t n_args - ); - ~assertion_info(); - assertion_info(const assertion_info&); - assertion_info(assertion_info&&); - assertion_info& operator=(const assertion_info&); - assertion_info& operator=(assertion_info&&); - - std::string_view action() const; - - const cpptrace::raw_trace& get_raw_trace() const; - const cpptrace::stacktrace& get_stacktrace() const; - - [[nodiscard]] std::string header(int width = 0, const color_scheme& scheme = get_color_scheme()) const; - [[nodiscard]] std::string tagline(const color_scheme& scheme = get_color_scheme()) const; - [[nodiscard]] std::string location() const; - [[nodiscard]] std::string statement(const color_scheme& scheme = get_color_scheme()) const; - [[nodiscard]] std::string print_binary_diagnostics(int width = 0, const color_scheme& scheme = get_color_scheme()) const; - [[nodiscard]] std::string print_extra_diagnostics(int width = 0, const color_scheme& scheme = get_color_scheme()) const; - [[nodiscard]] std::string print_stacktrace(int width = 0, const color_scheme& scheme = get_color_scheme()) const; - - [[nodiscard]] std::string to_string(int width = 0, const color_scheme& scheme = get_color_scheme()) const; - }; -} - -// ===================================================================================================================== -// || Library core || -// ===================================================================================================================== - -namespace libassert::detail { - /* - * C++ syntax analysis and literal formatting - */ - - // get current literal_format configuration for the thread - [[nodiscard]] LIBASSERT_EXPORT literal_format get_thread_current_literal_format(); - - // sets the current literal_format configuration for the thread - LIBASSERT_EXPORT void set_thread_current_literal_format(literal_format format); - - LIBASSERT_EXPORT literal_format set_literal_format( - std::string_view left_expression, - std::string_view right_expression, - std::string_view op, - bool integer_character - ); - LIBASSERT_EXPORT void restore_literal_format(literal_format); - // does the current literal format config have multiple formats - LIBASSERT_EXPORT bool has_multiple_formats(); - - [[nodiscard]] LIBASSERT_EXPORT std::pair decompose_expression( - std::string_view expression, - std::string_view target_op - ); - - /* - * System wrappers - */ - - [[nodiscard]] LIBASSERT_EXPORT std::string strerror_wrapper(int err); // stupid C stuff, stupid microsoft stuff - - /* - * assert diagnostics generation - */ - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - binary_diagnostics_descriptor generate_binary_diagnostic( - const A& left, - const B& right, - std::string_view left_str, - std::string_view right_str, - std::string_view op - ) { - constexpr bool either_is_character = isa || isa; - constexpr bool either_is_arithmetic = is_arith_not_bool_char || is_arith_not_bool_char; - literal_format previous_format = set_literal_format( - left_str, - right_str, - op, - either_is_character && either_is_arithmetic - ); - binary_diagnostics_descriptor descriptor( - left_str, - right_str, - generate_stringification(left), - generate_stringification(right), - has_multiple_formats() - ); - restore_literal_format(previous_format); - return descriptor; - } - - #define LIBASSERT_X(x) #x - #define LIBASSERT_Y(x) LIBASSERT_X(x) - constexpr const std::string_view errno_expansion = LIBASSERT_Y(errno); - #undef LIBASSERT_Y - #undef LIBASSERT_X - - struct pretty_function_name_wrapper { - const char* pretty_function; - }; - - inline void process_arg( // TODO: Don't inline - assertion_info& info, - size_t, - sv_span, - const pretty_function_name_wrapper& t - ) { - info.function = t.pretty_function; - } - - template - LIBASSERT_ATTR_COLD - // TODO - // NOLINTNEXTLINE(readability-function-cognitive-complexity) - void process_arg(assertion_info& info, size_t i, sv_span args_strings, const T& t) { - if constexpr(isa>) { - if(args_strings.data[i] == errno_expansion) { - info.extra_diagnostics.push_back({ "errno", bstringf("%2d \"%s\"", t, strerror_wrapper(t).c_str()) }); - return; - } - } else if constexpr(is_string_type) { - if(i == 0) { - if constexpr(std::is_pointer_v) { - if(t == nullptr) { - info.message = "(nullptr)"; - return; - } - } - info.message = t; - return; - } - } - info.extra_diagnostics.push_back({ args_strings.data[i], generate_stringification(t) }); - } - - template - LIBASSERT_ATTR_COLD - void process_args(assertion_info& info, sv_span args_strings, Args&... args) { - size_t i = 0; - (process_arg(info, i++, args_strings, args), ...); - (void)args_strings; - } -} - -/* - * Actual top-level assertion processing - */ - -namespace libassert::detail { - LIBASSERT_EXPORT void fail(const assertion_info& info); - - template - LIBASSERT_ATTR_COLD LIBASSERT_ATTR_NOINLINE - // TODO: Re-evaluate forwarding here. - void process_assert_fail( - expression_decomposer& decomposer, - const assert_static_parameters* params, - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - Args&&... args - ) { - const size_t sizeof_extra_diagnostics = sizeof...(args) - 1; // - 1 for pretty function signature - LIBASSERT_PRIMITIVE_DEBUG_ASSERT(sizeof...(args) <= params->args_strings.size); - assertion_info info( - params, - cpptrace::generate_raw_trace(), - sizeof_extra_diagnostics - ); - // process_args fills in the message, extra_diagnostics, and pretty_function - process_args(info, params->args_strings, args...); - // generate binary diagnostics - if constexpr(is_nothing) { - static_assert(is_nothing && !is_nothing); - if constexpr(isa) { - (void)decomposer; // suppress warning in msvc - } else { - info.binary_diagnostics = generate_binary_diagnostic( - decomposer.a, - true, - params->expr_str, - "true", - "==" - ); - } - } else { - auto [left_expression, right_expression] = decompose_expression(params->expr_str, C::op_string); - info.binary_diagnostics = generate_binary_diagnostic( - decomposer.a, - decomposer.b, - left_expression, - right_expression, - C::op_string - ); - } - // send off - fail(info); - } - - template - LIBASSERT_ATTR_COLD [[noreturn]] LIBASSERT_ATTR_NOINLINE - // TODO: Re-evaluate forwarding here. - void process_panic( - const assert_static_parameters* params, - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - Args&&... args - ) { - const size_t sizeof_extra_diagnostics = sizeof...(args) - 1; // - 1 for pretty function signature - LIBASSERT_PRIMITIVE_DEBUG_ASSERT(sizeof...(args) <= params->args_strings.size); - assertion_info info( - params, - cpptrace::generate_raw_trace(), - sizeof_extra_diagnostics - ); - // process_args fills in the message, extra_diagnostics, and pretty_function - process_args(info, params->args_strings, args...); - // send off - fail(info); - LIBASSERT_PRIMITIVE_PANIC("PANIC/UNREACHABLE failure handler returned"); - } - - // TODO: Re-evaluate benefit of this at all in non-cold path code - template - LIBASSERT_ATTR_COLD LIBASSERT_ATTR_NOINLINE [[nodiscard]] - expression_decomposer process_assert_fail_m( - expression_decomposer decomposer, - const assert_static_parameters* params, - Args&&... args - ) { - process_assert_fail(decomposer, params, std::forward(args)...); - return decomposer; - } - - template - LIBASSERT_ATTR_COLD LIBASSERT_ATTR_NOINLINE - void process_assert_fail_n( - expression_decomposer decomposer, - const assert_static_parameters* params, - Args&&... args - ) { - process_assert_fail(decomposer, params, std::forward(args)...); - } - - template - struct assert_value_wrapper { - T value; - }; - - template< - bool R, bool ret_lhs, bool value_is_lval_ref, - typename T, typename A, typename B, typename C - > - constexpr auto get_expression_return_value(T& value, expression_decomposer& decomposer) { - if constexpr(R) { - if constexpr(ret_lhs) { - if constexpr(std::is_lvalue_reference_v) { - return assert_value_wrapper{decomposer.take_lhs()}; - } else { - return assert_value_wrapper{std::move(decomposer.take_lhs())}; - } - } else { - if constexpr(value_is_lval_ref) { - return assert_value_wrapper{value}; - } else { - return assert_value_wrapper{std::move(value)}; - } - } - } - } -} - -#if LIBASSERT_IS_MSVC - #pragma warning(pop) -#endif - -#if LIBASSERT_IS_CLANG || LIBASSERT_IS_GCC || !LIBASSERT_NON_CONFORMANT_MSVC_PREPROCESSOR - // Macro mapping utility by William Swanson https://github.com/swansontec/map-macro/blob/master/map.h - #define LIBASSERT_EVAL0(...) __VA_ARGS__ - #define LIBASSERT_EVAL1(...) LIBASSERT_EVAL0(LIBASSERT_EVAL0(LIBASSERT_EVAL0(__VA_ARGS__))) - #define LIBASSERT_EVAL2(...) LIBASSERT_EVAL1(LIBASSERT_EVAL1(LIBASSERT_EVAL1(__VA_ARGS__))) - #define LIBASSERT_EVAL3(...) LIBASSERT_EVAL2(LIBASSERT_EVAL2(LIBASSERT_EVAL2(__VA_ARGS__))) - #define LIBASSERT_EVAL4(...) LIBASSERT_EVAL3(LIBASSERT_EVAL3(LIBASSERT_EVAL3(__VA_ARGS__))) - #define LIBASSERT_EVAL(...) LIBASSERT_EVAL4(LIBASSERT_EVAL4(LIBASSERT_EVAL4(__VA_ARGS__))) - #define LIBASSERT_MAP_END(...) - #define LIBASSERT_MAP_OUT - #define LIBASSERT_MAP_COMMA , - #define LIBASSERT_MAP_GET_END2() 0, LIBASSERT_MAP_END - #define LIBASSERT_MAP_GET_END1(...) LIBASSERT_MAP_GET_END2 - #define LIBASSERT_MAP_GET_END(...) LIBASSERT_MAP_GET_END1 - #define LIBASSERT_MAP_NEXT0(test, next, ...) next LIBASSERT_MAP_OUT - #define LIBASSERT_MAP_NEXT1(test, next) LIBASSERT_MAP_NEXT0(test, next, 0) - #define LIBASSERT_MAP_NEXT(test, next) LIBASSERT_MAP_NEXT1(LIBASSERT_MAP_GET_END test, next) - #define LIBASSERT_MAP0(f, x, peek, ...) f(x) LIBASSERT_MAP_NEXT(peek, LIBASSERT_MAP1)(f, peek, __VA_ARGS__) - #define LIBASSERT_MAP1(f, x, peek, ...) f(x) LIBASSERT_MAP_NEXT(peek, LIBASSERT_MAP0)(f, peek, __VA_ARGS__) - #define LIBASSERT_MAP_LIST_NEXT1(test, next) LIBASSERT_MAP_NEXT0(test, LIBASSERT_MAP_COMMA next, 0) - #define LIBASSERT_MAP_LIST_NEXT(test, next) LIBASSERT_MAP_LIST_NEXT1(LIBASSERT_MAP_GET_END test, next) - #define LIBASSERT_MAP_LIST0(f, x, peek, ...) \ - f(x) LIBASSERT_MAP_LIST_NEXT(peek, LIBASSERT_MAP_LIST1)(f, peek, __VA_ARGS__) - #define LIBASSERT_MAP_LIST1(f, x, peek, ...) \ - f(x) LIBASSERT_MAP_LIST_NEXT(peek, LIBASSERT_MAP_LIST0)(f, peek, __VA_ARGS__) - #define LIBASSERT_MAP(f, ...) LIBASSERT_EVAL(LIBASSERT_MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) -#else - // https://stackoverflow.com/a/29474124/15675011 - #define LIBASSERT_PLUS_TEXT_(x,y) x ## y - #define LIBASSERT_PLUS_TEXT(x, y) LIBASSERT_PLUS_TEXT_(x, y) - #define LIBASSERT_ARG_1(_1, ...) _1 - #define LIBASSERT_ARG_2(_1, _2, ...) _2 - #define LIBASSERT_ARG_3(_1, _2, _3, ...) _3 - #define LIBASSERT_ARG_40( _0, _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, \ - ...) _39 - #define LIBASSERT_OTHER_1(_1, ...) __VA_ARGS__ - #define LIBASSERT_OTHER_3(_1, _2, _3, ...) __VA_ARGS__ - #define LIBASSERT_EVAL0(...) __VA_ARGS__ - #define LIBASSERT_EVAL1(...) LIBASSERT_EVAL0(LIBASSERT_EVAL0(LIBASSERT_EVAL0(__VA_ARGS__))) - #define LIBASSERT_EVAL2(...) LIBASSERT_EVAL1(LIBASSERT_EVAL1(LIBASSERT_EVAL1(__VA_ARGS__))) - #define LIBASSERT_EVAL3(...) LIBASSERT_EVAL2(LIBASSERT_EVAL2(LIBASSERT_EVAL2(__VA_ARGS__))) - #define LIBASSERT_EVAL4(...) LIBASSERT_EVAL3(LIBASSERT_EVAL3(LIBASSERT_EVAL3(__VA_ARGS__))) - #define LIBASSERT_EVAL(...) LIBASSERT_EVAL4(LIBASSERT_EVAL4(LIBASSERT_EVAL4(__VA_ARGS__))) - #define LIBASSERT_EXPAND(x) x - #define LIBASSERT_MAP_SWITCH(...)\ - LIBASSERT_EXPAND(LIBASSERT_ARG_40(__VA_ARGS__, 2, 2, 2, 2, 2, 2, 2, 2, 2,\ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,\ - 2, 2, 2, 2, 2, 2, 2, 2, 2,\ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0)) - #define LIBASSERT_MAP_A(...) LIBASSERT_PLUS_TEXT(LIBASSERT_MAP_NEXT_, \ - LIBASSERT_MAP_SWITCH(0, __VA_ARGS__)) (LIBASSERT_MAP_B, __VA_ARGS__) - #define LIBASSERT_MAP_B(...) LIBASSERT_PLUS_TEXT(LIBASSERT_MAP_NEXT_, \ - LIBASSERT_MAP_SWITCH(0, __VA_ARGS__)) (LIBASSERT_MAP_A, __VA_ARGS__) - #define LIBASSERT_MAP_CALL(fn, Value) LIBASSERT_EXPAND(fn(Value)) - #define LIBASSERT_MAP_OUT - #define LIBASSERT_MAP_NEXT_2(...)\ - LIBASSERT_MAP_CALL(LIBASSERT_EXPAND(LIBASSERT_ARG_2(__VA_ARGS__)), \ - LIBASSERT_EXPAND(LIBASSERT_ARG_3(__VA_ARGS__))) \ - LIBASSERT_EXPAND(LIBASSERT_ARG_1(__VA_ARGS__)) \ - LIBASSERT_MAP_OUT \ - (LIBASSERT_EXPAND(LIBASSERT_ARG_2(__VA_ARGS__)), LIBASSERT_EXPAND(LIBASSERT_OTHER_3(__VA_ARGS__))) - #define LIBASSERT_MAP_NEXT_0(...) - #define LIBASSERT_MAP(...) LIBASSERT_EVAL(LIBASSERT_MAP_A(__VA_ARGS__)) -#endif - -#define LIBASSERT_STRINGIFY(x) #x, -#define LIBASSERT_COMMA , - -// Church boolean -#define LIBASSERT_IF(b) LIBASSERT_IF_##b -#define LIBASSERT_IF_true(t,...) t -#define LIBASSERT_IF_false(t,f,...) f - -#if LIBASSERT_IS_CLANG || LIBASSERT_IS_GCC - #if LIBASSERT_IS_GCC - #define LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_GCC \ - _Pragma("GCC diagnostic ignored \"-Wparentheses\"") \ - _Pragma("GCC diagnostic ignored \"-Wuseless-cast\"") // #49 - #define LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_CLANG - #define LIBASSERT_WARNING_PRAGMA_PUSH_GCC _Pragma("GCC diagnostic push") - #define LIBASSERT_WARNING_PRAGMA_POP_GCC _Pragma("GCC diagnostic pop") - #define LIBASSERT_WARNING_PRAGMA_PUSH_CLANG - #define LIBASSERT_WARNING_PRAGMA_POP_CLANG - #else - #define LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_CLANG \ - _Pragma("GCC diagnostic ignored \"-Wparentheses\"") \ - _Pragma("GCC diagnostic ignored \"-Woverloaded-shift-op-parentheses\"") - #define LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_GCC - #define LIBASSERT_WARNING_PRAGMA_PUSH_GCC - #define LIBASSERT_WARNING_PRAGMA_POP_GCC - #define LIBASSERT_WARNING_PRAGMA_PUSH_CLANG _Pragma("GCC diagnostic push") - #define LIBASSERT_WARNING_PRAGMA_POP_CLANG _Pragma("GCC diagnostic pop") - #endif -#else - #define LIBASSERT_WARNING_PRAGMA_PUSH_CLANG - #define LIBASSERT_WARNING_PRAGMA_POP_CLANG - #define LIBASSERT_WARNING_PRAGMA_PUSH_GCC - #define LIBASSERT_WARNING_PRAGMA_POP_GCC - #define LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_GCC - #define LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_CLANG -#endif - -namespace libassert { - inline void ERROR_ASSERTION_FAILURE_IN_CONSTEXPR_CONTEXT() { - // This non-constexpr method is called from an assertion in a constexpr context if a failure occurs. It is - // intentionally a no-op. - } -} - -// __PRETTY_FUNCTION__ used because __builtin_FUNCTION() used in source_location (like __FUNCTION__) is just the method -// name, not signature -// The arg strings at the very least must be static constexpr. Unfortunately static constexpr variables are not allowed -// in constexpr functions pre-C++23. -// TODO: Try to do a hybrid in C++20 with std::is_constant_evaluated? -#if defined(__cpp_constexpr) && __cpp_constexpr >= 202211L -// Can just use static constexpr everywhere -#define LIBASSERT_STATIC_DATA(name, type, expr_str, ...) \ - /* extra string here because of extra comma from map, also serves as terminator */ \ - /* LIBASSERT_STRINGIFY LIBASSERT_VA_ARGS because msvc */ \ - /* Trailing return type here to work around a gcc <= 9.2 bug */ \ - /* Oddly only affecting builds under -DNDEBUG https://godbolt.org/z/5Treozc4q */ \ - using libassert_params_t = libassert::detail::assert_static_parameters; \ - /* NOLINTNEXTLINE(*-avoid-c-arrays) */ \ - static constexpr std::string_view libassert_arg_strings[] = { \ - LIBASSERT_MAP(LIBASSERT_STRINGIFY LIBASSERT_VA_ARGS(__VA_ARGS__)) "" \ - }; \ - static constexpr libassert_params_t _libassert_params = { \ - name LIBASSERT_COMMA \ - type LIBASSERT_COMMA \ - expr_str LIBASSERT_COMMA \ - {} LIBASSERT_COMMA \ - {libassert_arg_strings, sizeof(libassert_arg_strings) / sizeof(std::string_view)} LIBASSERT_COMMA \ - }; \ - const libassert_params_t* libassert_params = &_libassert_params; -#else -#define LIBASSERT_STATIC_DATA(name, type, expr_str, ...) \ - using libassert_params_t = libassert::detail::assert_static_parameters; \ - /* NOLINTNEXTLINE(*-avoid-c-arrays) */ \ - const libassert_params_t* libassert_params = []() -> const libassert_params_t* { \ - static constexpr std::string_view libassert_arg_strings[] = { \ - LIBASSERT_MAP(LIBASSERT_STRINGIFY LIBASSERT_VA_ARGS(__VA_ARGS__)) "" \ - }; \ - static constexpr libassert_params_t _libassert_params = { \ - name LIBASSERT_COMMA \ - type LIBASSERT_COMMA \ - expr_str LIBASSERT_COMMA \ - {} LIBASSERT_COMMA \ - {libassert_arg_strings, sizeof(libassert_arg_strings) / sizeof(std::string_view)} LIBASSERT_COMMA \ - }; \ - return &_libassert_params; \ - }(); -#endif - -// Note about statement expressions: These are needed for two reasons. The first is putting the arg string array and -// source location structure in .rodata rather than on the stack, the second is a _Pragma for warnings which isn't -// allowed in the middle of an expression by GCC. The semantics are similar to a function return: -// Given M m; in parent scope, ({ m; }) is an rvalue M&& rather than an lvalue -// ({ M m; m; }) doesn't move, it copies -// ({ M{}; }) does move -// Of relevance to this: in foo(__extension__ ({ M{1} + M{1}; })); the lifetimes of the M{1} objects end during the -// statement expression but the lifetime of the returned object is extend to the end of the full foo() expression. -// A wrapper struct is used here to return an lvalue reference from a gcc statement expression. -// Note: There is a current issue with tarnaries: auto x = assert(b ? y : y); must copy y. This can be fixed with -// lambdas but that's potentially very expensive compile-time wise. Need to investigate further. -// Note: libassert::detail::expression_decomposer(libassert::detail::expression_decomposer{} << expr) done for ternary -#if LIBASSERT_IS_MSVC - #define LIBASSERT_INVOKE_VAL_PRETTY_FUNCTION_ARG ,libassert::detail::pretty_function_name_wrapper{libassert_msvc_pfunc} -#else - #define LIBASSERT_INVOKE_VAL_PRETTY_FUNCTION_ARG ,libassert::detail::pretty_function_name_wrapper{LIBASSERT_PFUNC} -#endif -#define LIBASSERT_PRETTY_FUNCTION_ARG ,libassert::detail::pretty_function_name_wrapper{LIBASSERT_PFUNC} -#if LIBASSERT_IS_CLANG // -Wall in clang - #define LIBASSERT_IGNORE_UNUSED_VALUE _Pragma("GCC diagnostic ignored \"-Wunused-value\"") -#else - #define LIBASSERT_IGNORE_UNUSED_VALUE -#endif - -#define LIBASSERT_BREAKPOINT_IF_DEBUGGING() \ - do \ - if(libassert::is_debugger_present()) { \ - LIBASSERT_BREAKPOINT(); \ - } \ - while(0) - -#ifdef LIBASSERT_BREAK_ON_FAIL - #define LIBASSERT_BREAKPOINT_IF_DEBUGGING_ON_FAIL() LIBASSERT_BREAKPOINT_IF_DEBUGGING() -#else - #define LIBASSERT_BREAKPOINT_IF_DEBUGGING_ON_FAIL() -#endif - -#define LIBASSERT_INVOKE(expr, name, type, failaction, ...) \ - /* must push/pop out here due to nasty clang bug https://github.com/llvm/llvm-project/issues/63897 */ \ - /* must do awful stuff to workaround differences in where gcc and clang allow these directives to go */ \ - do { \ - LIBASSERT_WARNING_PRAGMA_PUSH_CLANG \ - LIBASSERT_IGNORE_UNUSED_VALUE \ - LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_CLANG \ - LIBASSERT_WARNING_PRAGMA_PUSH_GCC \ - LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_GCC \ - auto libassert_decomposer = libassert::detail::expression_decomposer( \ - libassert::detail::expression_decomposer{} << expr \ - ); \ - LIBASSERT_WARNING_PRAGMA_POP_GCC \ - if(LIBASSERT_STRONG_EXPECT(!static_cast(libassert_decomposer.get_value()), 0)) { \ - libassert::ERROR_ASSERTION_FAILURE_IN_CONSTEXPR_CONTEXT(); \ - LIBASSERT_BREAKPOINT_IF_DEBUGGING_ON_FAIL(); \ - failaction \ - LIBASSERT_STATIC_DATA(name, libassert::assert_type::type, #expr, __VA_ARGS__) \ - if constexpr(sizeof libassert_decomposer > 32) { \ - libassert::detail::process_assert_fail( \ - libassert_decomposer, \ - libassert_params \ - LIBASSERT_VA_ARGS(__VA_ARGS__) LIBASSERT_PRETTY_FUNCTION_ARG \ - ); \ - } else { \ - /* std::move it to assert_fail_m, will be moved back to r */ \ - libassert::detail::process_assert_fail_n( \ - std::move(libassert_decomposer), \ - libassert_params \ - LIBASSERT_VA_ARGS(__VA_ARGS__) LIBASSERT_PRETTY_FUNCTION_ARG \ - ); \ - } \ - } \ - LIBASSERT_WARNING_PRAGMA_POP_CLANG \ - } while(false) \ - -#define LIBASSERT_INVOKE_PANIC(name, type, ...) \ - do { \ - libassert::ERROR_ASSERTION_FAILURE_IN_CONSTEXPR_CONTEXT(); \ - LIBASSERT_BREAKPOINT_IF_DEBUGGING_ON_FAIL(); \ - LIBASSERT_STATIC_DATA(name, libassert::assert_type::type, "", __VA_ARGS__) \ - libassert::detail::process_panic( \ - libassert_params \ - LIBASSERT_VA_ARGS(__VA_ARGS__) LIBASSERT_PRETTY_FUNCTION_ARG \ - ); \ - } while(false) \ - -// Workaround for gcc bug 105734 / libassert bug #24 -#define LIBASSERT_DESTROY_DECOMPOSER libassert_decomposer.~expression_decomposer() /* NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move) */ -#if LIBASSERT_IS_GCC - #if __GNUC__ == 12 && __GNUC_MINOR__ == 1 - namespace libassert::detail { - template constexpr void destroy(T& t) { - t.~T(); - } - } - #undef LIBASSERT_DESTROY_DECOMPOSER - #define LIBASSERT_DESTROY_DECOMPOSER libassert::detail::destroy(libassert_decomposer) - #endif -#endif -#if LIBASSERT_IS_CLANG || LIBASSERT_IS_GCC - // Extra set of parentheses here because clang treats __extension__ as a low-precedence unary operator which interferes - // with decltype(auto) in an expression like decltype(auto) x = __extension__ ({...}).y; - #define LIBASSERT_STMTEXPR(B, R) (__extension__ ({ B R })) - #define LIBASSERT_STATIC_CAST_TO_BOOL(x) static_cast(x) -#else - #define LIBASSERT_STMTEXPR(B, R) [&](const char* libassert_msvc_pfunc) { B return R }(LIBASSERT_PFUNC) - // Workaround for msvc bug - #define LIBASSERT_STATIC_CAST_TO_BOOL(x) libassert::detail::static_cast_to_bool(x) - namespace libassert::detail { - template constexpr bool static_cast_to_bool(T&& t) { - return static_cast(t); - } - } -#endif -#define LIBASSERT_INVOKE_VAL(expr, doreturn, check_expression, name, type, failaction, ...) \ - /* must push/pop out here due to nasty clang bug https://github.com/llvm/llvm-project/issues/63897 */ \ - /* must do awful stuff to workaround differences in where gcc and clang allow these directives to go */ \ - LIBASSERT_WARNING_PRAGMA_PUSH_CLANG \ - LIBASSERT_IGNORE_UNUSED_VALUE \ - LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_CLANG \ - LIBASSERT_STMTEXPR( \ - LIBASSERT_WARNING_PRAGMA_PUSH_GCC \ - LIBASSERT_EXPRESSION_DECOMP_WARNING_PRAGMA_GCC \ - auto libassert_decomposer = libassert::detail::expression_decomposer( \ - libassert::detail::expression_decomposer{} << expr \ - ); \ - LIBASSERT_WARNING_PRAGMA_POP_GCC \ - decltype(auto) libassert_value = libassert_decomposer.get_value(); \ - constexpr bool libassert_ret_lhs = libassert_decomposer.ret_lhs(); \ - if constexpr(check_expression) { \ - /* For *some* godforsaken reason static_cast causes an ICE in MSVC here. Something very specific */ \ - /* about casting a decltype(auto) value inside a lambda. Workaround is to put it in a wrapper. */ \ - /* https://godbolt.org/z/Kq8Wb6q5j https://godbolt.org/z/nMnqnsMYx */ \ - if(LIBASSERT_STRONG_EXPECT(!LIBASSERT_STATIC_CAST_TO_BOOL(libassert_value), 0)) { \ - libassert::ERROR_ASSERTION_FAILURE_IN_CONSTEXPR_CONTEXT(); \ - LIBASSERT_BREAKPOINT_IF_DEBUGGING_ON_FAIL(); \ - failaction \ - LIBASSERT_STATIC_DATA(name, libassert::assert_type::type, #expr, __VA_ARGS__) \ - if constexpr(sizeof libassert_decomposer > 32) { \ - libassert::detail::process_assert_fail( \ - libassert_decomposer, \ - libassert_params \ - LIBASSERT_VA_ARGS(__VA_ARGS__) LIBASSERT_INVOKE_VAL_PRETTY_FUNCTION_ARG \ - ); \ - } else { \ - /* std::move it to assert_fail_m, will be moved back to r */ \ - auto libassert_r = libassert::detail::process_assert_fail_m( \ - std::move(libassert_decomposer), \ - libassert_params \ - LIBASSERT_VA_ARGS(__VA_ARGS__) LIBASSERT_INVOKE_VAL_PRETTY_FUNCTION_ARG \ - ); \ - /* can't move-assign back to decomposer if it holds reference members */ \ - LIBASSERT_DESTROY_DECOMPOSER; \ - new (&libassert_decomposer) libassert::detail::expression_decomposer(std::move(libassert_r)); \ - } \ - } \ - }, \ - /* Note: std::launder needed in 17 in case of placement new / move shenanigans above */ \ - /* https://timsong-cpp.github.io/cppwp/n4659/basic.life#8.3 */ \ - /* Note: Somewhat relying on this call being inlined so inefficiency is eliminated */ \ - libassert::detail::get_expression_return_value< \ - doreturn LIBASSERT_COMMA \ - libassert_ret_lhs LIBASSERT_COMMA \ - std::is_lvalue_reference_v \ - >(libassert_value, *std::launder(&libassert_decomposer)); \ - ) LIBASSERT_IF(doreturn)(.value,) \ - LIBASSERT_WARNING_PRAGMA_POP_CLANG - -#ifdef NDEBUG - #define LIBASSERT_ASSUME_ACTION LIBASSERT_UNREACHABLE_CALL; -#else - #define LIBASSERT_ASSUME_ACTION -#endif - -// assertion macros - -// Debug assert -#ifndef NDEBUG - #define LIBASSERT_DEBUG_ASSERT(expr, ...) LIBASSERT_INVOKE(expr, "DEBUG_ASSERT", debug_assertion, , __VA_ARGS__) -#else - #define LIBASSERT_DEBUG_ASSERT(expr, ...) (void)0 -#endif - -// Assert -#define LIBASSERT_ASSERT(expr, ...) LIBASSERT_INVOKE(expr, "ASSERT", assertion, , __VA_ARGS__) -// lowercase version intentionally done outside of the include guard here - -// Assume -#define LIBASSERT_ASSUME(expr, ...) LIBASSERT_INVOKE(expr, "ASSUME", assumption, LIBASSERT_ASSUME_ACTION, __VA_ARGS__) - -// Panic -#define LIBASSERT_PANIC(...) LIBASSERT_INVOKE_PANIC("PANIC", panic, __VA_ARGS__) - -// Unreachable -#ifndef NDEBUG - #define LIBASSERT_UNREACHABLE(...) LIBASSERT_INVOKE_PANIC("UNREACHABLE", unreachable, __VA_ARGS__) -#else - #define LIBASSERT_UNREACHABLE(...) LIBASSERT_UNREACHABLE_CALL -#endif - -// value variants - -#ifndef NDEBUG - #define LIBASSERT_DEBUG_ASSERT_VAL(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, true, "DEBUG_ASSERT_VAL", debug_assertion, , __VA_ARGS__) -#else - #define LIBASSERT_DEBUG_ASSERT_VAL(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, false, "DEBUG_ASSERT_VAL", debug_assertion, , __VA_ARGS__) -#endif - -#define LIBASSERT_ASSUME_VAL(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, true, "ASSUME_VAL", assumption, LIBASSERT_ASSUME_ACTION, __VA_ARGS__) - -#define LIBASSERT_ASSERT_VAL(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, true, "ASSERT_VAL", assertion, , __VA_ARGS__) - -// non-prefixed versions - -#ifndef LIBASSERT_PREFIX_ASSERTIONS - #if LIBASSERT_IS_CLANG || LIBASSERT_IS_GCC || !LIBASSERT_NON_CONFORMANT_MSVC_PREPROCESSOR - #define DEBUG_ASSERT(...) LIBASSERT_DEBUG_ASSERT(__VA_ARGS__) - #define ASSERT(...) LIBASSERT_ASSERT(__VA_ARGS__) - #define ASSUME(...) LIBASSERT_ASSUME(__VA_ARGS__) - #define PANIC(...) LIBASSERT_PANIC(__VA_ARGS__) - #define UNREACHABLE(...) LIBASSERT_UNREACHABLE(__VA_ARGS__) - #define DEBUG_ASSERT_VAL(...) LIBASSERT_DEBUG_ASSERT_VAL(__VA_ARGS__) - #define ASSUME_VAL(...) LIBASSERT_ASSUME_VAL(__VA_ARGS__) - #define ASSERT_VAL(...) LIBASSERT_ASSERT_VAL(__VA_ARGS__) - #else - // because of course msvc - #define DEBUG_ASSERT LIBASSERT_DEBUG_ASSERT - #define ASSERT LIBASSERT_ASSERT - #define ASSUME LIBASSERT_ASSUME - #define PANIC LIBASSERT_PANIC - #define UNREACHABLE LIBASSERT_UNREACHABLE - #define DEBUG_ASSERT_VAL LIBASSERT_DEBUG_ASSERT_VAL - #define ASSUME_VAL LIBASSERT_ASSUME_VAL - #define ASSERT_VAL LIBASSERT_ASSERT_VAL - #endif -#endif - -// Lowercase variants - -#ifdef LIBASSERT_LOWERCASE - #ifndef NDEBUG - #define debug_assert(expr, ...) LIBASSERT_INVOKE(expr, "debug_assert", debug_assertion, , __VA_ARGS__) - #else - #define debug_assert(expr, ...) (void)0 - #endif -#endif - -#ifdef LIBASSERT_LOWERCASE - #ifndef NDEBUG - #define debug_assert_val(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, true, "debug_assert_val", debug_assertion, , __VA_ARGS__) - #else - #define debug_assert_val(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, false, "debug_assert_val", debug_assertion, , __VA_ARGS__) - #endif -#endif - -#ifdef LIBASSERT_LOWERCASE - #define assert_val(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, true, "assert_val", assertion, , __VA_ARGS__) -#endif - -// Wrapper macro to allow support for C++26's user generated static_assert messages. -// The backup message version also allows for the user to provide a backup version that will -// be used if the compiler does not support user generated messages. -// More info on user generated static_assert's -// can be found here: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2741r1.pdf -// -// Currently the functionality works as such. If we are in a C++26 environment, the user generated message will be used. -// If we are not in a C++26 environment, then either the static_assert will be used without a message or the backup message. -// TODO: Maybe give these a better name? Ideally one that is shorter and more descriptive? -// TODO: Maybe add a helper to make passing user generated static_assert messages easier? -#if defined(__cpp_static_assert) && __cpp_static_assert >= 202306L - #ifdef LIBASSERT_LOWERCASE - #define libassert_user_static_assert(cond, constant) static_assert(cond, constant) - #define libassert_user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, constant) - #define user_static_assert(cond, constant) static_assert(cond, constant) - #define user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, constant) - #else - #define LIBASSERT_USER_STATIC_ASSERT(cond, constant) static_assert(cond, constant) - #define LIBASSERT_USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, constant) - #define USER_STATIC_ASSERT(cond, constant) static_assert(cond, constant) - #define USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, constant) - #endif -#else - #ifdef LIBASSERT_LOWERCASE - #define libassert_user_static_assert(cond, constant) static_assert(cond) - #define libassert_user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, msg) - #define user_static_assert(cond, constant) static_assert(cond) - #define user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, msg) - #else - #define LIBASSERT_USER_STATIC_ASSERT(cond, constant) static_assert(cond) - #define LIBASSERT_USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, msg) - #define USER_STATIC_ASSERT(cond, constant) static_assert(cond) - #define USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, msg) - #endif -#endif - - -#endif // LIBASSERT_HPP - -// Intentionally done outside the include guard. Libc++ leaks `assert` (among other things), so the include for -// assert.hpp should go after other includes when using -DLIBASSERT_LOWERCASE. -#ifdef LIBASSERT_LOWERCASE - #ifdef assert - #undef assert - #endif - #ifndef NDEBUG - #define assert(expr, ...) LIBASSERT_INVOKE(expr, "assert", assertion, , __VA_ARGS__) - #else - #define assert(expr, ...) LIBASSERT_INVOKE(expr, "assert", assertion, , __VA_ARGS__) - #endif -#endif diff --git a/src/contrib/libassert/expression-decomposition.hpp b/src/contrib/libassert/expression-decomposition.hpp deleted file mode 100644 index 7ecf8c0..0000000 --- a/src/contrib/libassert/expression-decomposition.hpp +++ /dev/null @@ -1,330 +0,0 @@ -#ifndef LIBASSERT_EXPRESSION_DECOMPOSITION_HPP -#define LIBASSERT_EXPRESSION_DECOMPOSITION_HPP - -#include -#include -#include - -#include -#include - -// ===================================================================================================================== -// || Expression decomposition micro-library || -// ===================================================================================================================== - -namespace libassert::detail { - // Lots of boilerplate - // Using int comparison functions here to support proper signed comparisons. Need to make sure - // assert(map.count(1) == 2) doesn't produce a warning. It wouldn't under normal circumstances - // but it would in this library due to the parameters being forwarded down a long chain. - // And we want to provide as much robustness as possible anyways. - // Copied and pasted from https://en.cppreference.com/w/cpp/utility/intcmp - // Not using std:: versions because library is targeting C++17 - template - [[nodiscard]] constexpr bool cmp_equal(T t, U u) { - using UT = std::make_unsigned_t; - using UU = std::make_unsigned_t; - if constexpr(std::is_signed_v == std::is_signed_v) { - return t == u; - } else if constexpr(std::is_signed_v) { - return t >= 0 && UT(t) == u; - } else { - return u >= 0 && t == UU(u); - } - } - - template - [[nodiscard]] constexpr bool cmp_not_equal(T t, U u) { - return !cmp_equal(t, u); - } - - template - [[nodiscard]] constexpr bool cmp_less(T t, U u) { - using UT = std::make_unsigned_t; - using UU = std::make_unsigned_t; - if constexpr(std::is_signed_v == std::is_signed_v) { - return t < u; - } else if constexpr(std::is_signed_v) { - return t < 0 || UT(t) < u; - } else { - return u >= 0 && t < UU(u); - } - } - - template - [[nodiscard]] constexpr bool cmp_greater(T t, U u) { - return cmp_less(u, t); - } - - template - [[nodiscard]] constexpr bool cmp_less_equal(T t, U u) { - return !cmp_less(u, t); - } - - template - [[nodiscard]] constexpr bool cmp_greater_equal(T t, U u) { - return !cmp_less(t, u); - } - - // Lots of boilerplate - // std:: implementations don't allow two separate types for lhs/rhs - // Note: is this macro potentially bad when it comes to debugging(?) - namespace ops { - #define LIBASSERT_GEN_OP_BOILERPLATE(name, op) struct name { \ - static constexpr std::string_view op_string = #op; \ - template \ - LIBASSERT_ATTR_COLD [[nodiscard]] \ - constexpr decltype(auto) operator()(A&& lhs, B&& rhs) const { /* no need to forward ints */ \ - return std::forward(lhs) op std::forward(rhs); \ - } \ - } - #define LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL(name, op, cmp) struct name { \ - static constexpr std::string_view op_string = #op; \ - template \ - LIBASSERT_ATTR_COLD [[nodiscard]] \ - constexpr decltype(auto) operator()(A&& lhs, B&& rhs) const { /* no need to forward ints */ \ - if constexpr(is_integral_and_not_bool && is_integral_and_not_bool) return cmp(lhs, rhs); \ - else return std::forward(lhs) op std::forward(rhs); \ - } \ - } - LIBASSERT_GEN_OP_BOILERPLATE(shl, <<); - LIBASSERT_GEN_OP_BOILERPLATE(shr, >>); - #if __cplusplus >= 202002L - LIBASSERT_GEN_OP_BOILERPLATE(spaceship, <=>); - #endif - #ifdef LIBASSERT_SAFE_COMPARISONS - // TODO: Make a SAFE() wrapper... - LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL(eq, ==, cmp_equal); - LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL(neq, !=, cmp_not_equal); - LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL(gt, >, cmp_greater); - LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL(lt, <, cmp_less); - LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL(gteq, >=, cmp_greater_equal); - LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL(lteq, <=, cmp_less_equal); - #else - LIBASSERT_GEN_OP_BOILERPLATE(eq, ==); - LIBASSERT_GEN_OP_BOILERPLATE(neq, !=); - LIBASSERT_GEN_OP_BOILERPLATE(gt, >); - LIBASSERT_GEN_OP_BOILERPLATE(lt, <); - LIBASSERT_GEN_OP_BOILERPLATE(gteq, >=); - LIBASSERT_GEN_OP_BOILERPLATE(lteq, <=); - #endif - LIBASSERT_GEN_OP_BOILERPLATE(band, &); - LIBASSERT_GEN_OP_BOILERPLATE(bxor, ^); - LIBASSERT_GEN_OP_BOILERPLATE(bor, |); - #ifdef LIBASSERT_DECOMPOSE_BINARY_LOGICAL - struct land { - static constexpr std::string_view op_string = "&&"; - template - LIBASSERT_ATTR_COLD [[nodiscard]] - constexpr decltype(auto) operator()(A&& lhs, B&& rhs) const { - // Go out of the way to support old-style ASSERT(foo && "Message") - #if LIBASSERT_IS_GCC - if constexpr(is_string_literal) { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wnonnull-compare" - return std::forward(lhs) && std::forward(rhs); - #pragma GCC diagnostic pop - } else { - return std::forward(lhs) && std::forward(rhs); - } - #else - return std::forward(lhs) && std::forward(rhs); - #endif - } - }; - LIBASSERT_GEN_OP_BOILERPLATE(lor, ||); - #endif - LIBASSERT_GEN_OP_BOILERPLATE(assign, =); - LIBASSERT_GEN_OP_BOILERPLATE(add_assign, +=); - LIBASSERT_GEN_OP_BOILERPLATE(sub_assign, -=); - LIBASSERT_GEN_OP_BOILERPLATE(mul_assign, *=); - LIBASSERT_GEN_OP_BOILERPLATE(div_assign, /=); - LIBASSERT_GEN_OP_BOILERPLATE(mod_assign, %=); - LIBASSERT_GEN_OP_BOILERPLATE(shl_assign, <<=); - LIBASSERT_GEN_OP_BOILERPLATE(shr_assign, >>=); - LIBASSERT_GEN_OP_BOILERPLATE(band_assign, &=); - LIBASSERT_GEN_OP_BOILERPLATE(bxor_assign, ^=); - LIBASSERT_GEN_OP_BOILERPLATE(bor_assign, |=); - #undef LIBASSERT_GEN_OP_BOILERPLATE - #undef LIBASSERT_GEN_OP_BOILERPLATE_SPECIAL - } - - // I learned this automatic expression decomposition trick from lest: - // https://github.com/martinmoene/lest/blob/master/include/lest/lest.hpp#L829-L853 - // - // I have improved upon the trick by supporting more operators and generally improving - // functionality. - // - // Some cases to test and consider: - // - // Expression Parsed as - // Basic: - // false << false - // a == b (<< a) == b - // - // Equal precedence following: - // a << b (<< a) << b - // a << b << c ((<< a) << b) << c - // a << b + c (<< a) << (b + c) - // a << b < c ((<< a) << b) < c // edge case - // - // Higher precedence following: - // a + b << (a + b) - // a + b + c << ((a + b) + c) - // a + b * c << (a + (b * c)) - // a + b < c (<< (a + b)) < c - // - // Lower precedence following: - // a < b (<< a) < b - // a < b < c ((<< a) < b) < c - // a < b + c (<< a) < (b + c) - // a < b == c ((<< a) < b) == c // edge case - - template - struct expression_decomposer { - A a; - B b; - explicit constexpr expression_decomposer() = default; - ~expression_decomposer() = default; - // not copyable - constexpr expression_decomposer(const expression_decomposer&) = delete; - constexpr expression_decomposer& operator=(const expression_decomposer&) = delete; - // allow move construction - constexpr expression_decomposer(expression_decomposer&&) - #if !LIBASSERT_IS_GCC || __GNUC__ >= 10 // gcc 9 has some issue with the move constructor being noexcept - noexcept - #endif - = default; - constexpr expression_decomposer& operator=(expression_decomposer&&) = delete; - // value constructors - template, int> = 0> - // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) // TODO - explicit constexpr expression_decomposer(U&& _a) : a(std::forward(_a)) {} - template - explicit constexpr expression_decomposer(U&& _a, V&& _b) : a(std::forward(_a)), b(std::forward(_b)) {} - /* Ownership logic: - * One of two things can happen to this class - * - Either it is composed with another operation - * + The value of the subexpression represented by this is computed (either get_value() - * or operator bool), either A& or C()(a, b) - * + Or, just the lhs is moved B is nothing - * - Or this class represents the whole expression - * + The value is computed (either A& or C()(a, b)) - * + a and b are referenced freely - * + Either the value is taken or a is moved out - * Ensuring the value is only computed once is left to the assert handler - */ - [[nodiscard]] - constexpr decltype(auto) get_value() { - if constexpr(is_nothing) { - static_assert(is_nothing && !is_nothing); - return (a); - } else { - return C()(a, b); - } - } - [[nodiscard]] - constexpr operator bool() { // for ternary support - return (bool)get_value(); - } - // return true if the lhs should be returned, false if full computed value should be - [[nodiscard]] - constexpr bool ret_lhs() { - static_assert(!is_nothing); - static_assert(is_nothing == is_nothing); - if constexpr(is_nothing) { - // if there is no top-level binary operation, A is the only thing that can be returned - return true; - } else { - // return LHS for the following types; - return C::op_string == "==" - || C::op_string == "!=" - || C::op_string == "<" - || C::op_string == ">" - || C::op_string == "<=" - || C::op_string == ">=" - || C::op_string == "&&" - || C::op_string == "||"; - // don't return LHS for << >> & ^ | and all assignments - } - } - [[nodiscard]] - constexpr A take_lhs() { // should only be called if ret_lhs() == true - if constexpr(std::is_lvalue_reference_v) { - return ((((a)))); - } else { - return std::move(a); - } - } - // Need overloads for operators with precedence <= bitshift - // TODO: spaceship operator? - // Note: Could decompose more than just comparison and boolean operators, but it would take - // a lot of work and I don't think it's beneficial for this library. - template [[nodiscard]] constexpr auto operator<<(O&& operand) && { - using Q = std::conditional_t, std::remove_reference_t, O>; - if constexpr(is_nothing) { - static_assert(is_nothing && is_nothing); - return expression_decomposer(std::forward(operand)); - } else if constexpr(is_nothing) { - static_assert(is_nothing); - return expression_decomposer(std::forward(a), std::forward(operand)); - } else { - static_assert(!is_nothing); - return expression_decomposer( - std::forward(get_value()), - std::forward(operand) - ); - } - } - #define LIBASSERT_GEN_OP_BOILERPLATE(functor, op) \ - template [[nodiscard]] constexpr auto operator op(O&& operand) && { \ - static_assert(!is_nothing); \ - using Q = std::conditional_t, std::remove_reference_t, O>; \ - if constexpr(is_nothing) { \ - static_assert(is_nothing); \ - return expression_decomposer(std::forward(a), std::forward(operand)); \ - } else { \ - static_assert(!is_nothing); \ - using V = decltype(get_value()); \ - return expression_decomposer(std::forward(get_value()), std::forward(operand)); \ - } \ - } - LIBASSERT_GEN_OP_BOILERPLATE(ops::shr, >>) - #if __cplusplus >= 202002L - LIBASSERT_GEN_OP_BOILERPLATE(ops::spaceship, <=>) - #endif - LIBASSERT_GEN_OP_BOILERPLATE(ops::eq, ==) - LIBASSERT_GEN_OP_BOILERPLATE(ops::neq, !=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::gt, >) - LIBASSERT_GEN_OP_BOILERPLATE(ops::lt, <) - LIBASSERT_GEN_OP_BOILERPLATE(ops::gteq, >=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::lteq, <=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::band, &) - LIBASSERT_GEN_OP_BOILERPLATE(ops::bxor, ^) - LIBASSERT_GEN_OP_BOILERPLATE(ops::bor, |) - #ifdef LIBASSERT_DECOMPOSE_BINARY_LOGICAL - LIBASSERT_GEN_OP_BOILERPLATE(ops::land, &&) - LIBASSERT_GEN_OP_BOILERPLATE(ops::lor, ||) - #endif - LIBASSERT_GEN_OP_BOILERPLATE(ops::assign, =) // NOLINT(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator) - LIBASSERT_GEN_OP_BOILERPLATE(ops::add_assign, +=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::sub_assign, -=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::mul_assign, *=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::div_assign, /=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::mod_assign, %=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::shl_assign, <<=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::shr_assign, >>=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::band_assign, &=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::bxor_assign, ^=) - LIBASSERT_GEN_OP_BOILERPLATE(ops::bor_assign, |=) - #undef LIBASSERT_GEN_OP_BOILERPLATE - }; - - // for ternary support - template - expression_decomposer(U&&) -> expression_decomposer< - std::conditional_t, std::remove_reference_t, U> - >; -} - -#endif diff --git a/src/contrib/libassert/platform.hpp b/src/contrib/libassert/platform.hpp deleted file mode 100644 index 1dc19a2..0000000 --- a/src/contrib/libassert/platform.hpp +++ /dev/null @@ -1,302 +0,0 @@ -#ifndef LIBASSERT_PLATFORM_HPP -#define LIBASSERT_PLATFORM_HPP - -// Copyright (c) 2021-2024 Jeremy Rifkin under the MIT license -// https://github.com/jeremy-rifkin/libassert - -// ===================================================================================================================== -// || Preprocessor stuff || -// ===================================================================================================================== - -// Set the C++ version number based on if we are on a dumb compiler like MSVC or not. -#ifdef _MSVC_LANG - #define LIBASSERT_CPLUSPLUS _MSVC_LANG -#else - #define LIBASSERT_CPLUSPLUS __cplusplus -#endif - -#if LIBASSERT_CPLUSPLUS >= 202302L - #define LIBASSERT_STD_VER 23 -#elif LIBASSERT_CPLUSPLUS >= 202002L - #define LIBASSERT_STD_VER 20 -#elif LIBASSERT_CPLUSPLUS >= 201703L - #define LIBASSERT_STD_VER 17 -#else - #error "libassert requires C++17 or newer" -#endif - -/// -/// Detect compiler versions. -/// - -#if defined(__clang__) && !defined(__ibmxl__) - #define LIBASSERT_IS_CLANG 1 - #define LIBASSERT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -#else - #define LIBASSERT_IS_CLANG 0 - #define LIBASSERT_CLANG_VERSION 0 -#endif - -#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !defined(__INTEL_COMPILER) - #define LIBASSERT_IS_GCC 1 - #define LIBASSERT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#else - #define LIBASSERT_IS_GCC 0 - #define LIBASSERT_GCC_VERSION 0 -#endif - -#if defined(_MSC_VER) && !defined(__clang__) // clang on windows defines _MSC_VER - #define LIBASSERT_IS_MSVC 1 - #define LIBASSERT_MSVC_VERSION _MSC_VER -#else - #define LIBASSERT_IS_MSVC 0 - #define LIBASSERT_MSVC_VERSION 0 -#endif - -#ifdef __INTEL_COMPILER - #define LIBASSERT_IS_ICC 1 -#else - #define LIBASSERT_IS_ICC 0 -#endif - -#ifdef __INTEL_LLVM_COMPILER - #define LIBASSERT_IS_ICX 1 -#else - #define LIBASSERT_IS_ICX 0 -#endif - -/// -/// Detect standard library versions. -/// - -// libstdc++ -#ifdef _GLIBCXX_RELEASE - #define LIBASSERT_GLIBCXX_RELEASE _GLIBCXX_RELEASE -#else - #define LIBASSERT_GLIBCXX_RELEASE 0 -#endif - -#ifdef _LIBCPP_VERSION - #define LIBASSERT_LIBCPP_VERSION _LIBCPP_VERSION -#else - #define LIBASSERT_LIBCPP_VERSION 0 -#endif - -/// -/// Helper macros for compiler attributes. -/// - -#ifdef __has_cpp_attribute - #define LIBASSERT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else - #define LIBASSERT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -/// -/// Compiler attribute support. -/// - -#if LIBASSERT_HAS_CPP_ATTRIBUTE(nodiscard) && LIBASSERT_STD_VER >= 20 - #define LIBASSERT_ATTR_NODISCARD_MSG(msg) [[nodiscard(msg)]] -#else // Assume we have normal C++17 nodiscard support. - #define LIBASSERT_ATTR_NODISCARD_MSG(msg) [[nodiscard]] -#endif - - -#if LIBASSERT_HAS_CPP_ATTRIBUTE(no_unique_address) - #define LIBASSERT_ATTR_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else - #define LIBASSERT_ATTR_NO_UNIQUE_ADDRESS -#endif - -/// -/// General project macros -/// - -#ifdef _WIN32 - #define LIBASSERT_EXPORT_ATTR __declspec(dllexport) - #define LIBASSERT_IMPORT_ATTR __declspec(dllimport) -#else - #define LIBASSERT_EXPORT_ATTR __attribute__((visibility("default"))) - #define LIBASSERT_IMPORT_ATTR __attribute__((visibility("default"))) -#endif - -#ifdef LIBASSERT_STATIC_DEFINE - #define LIBASSERT_EXPORT - #define LIBASSERT_NO_EXPORT -#else - #ifndef LIBASSERT_EXPORT - #ifdef libassert_lib_EXPORTS - /* We are building this library */ - #define LIBASSERT_EXPORT LIBASSERT_EXPORT_ATTR - #else - /* We are using this library */ - #define LIBASSERT_EXPORT LIBASSERT_IMPORT_ATTR - #endif - #endif -#endif - -#if LIBASSERT_IS_CLANG || LIBASSERT_IS_GCC - #define LIBASSERT_PFUNC __extension__ __PRETTY_FUNCTION__ - #define LIBASSERT_ATTR_COLD [[gnu::cold]] - #define LIBASSERT_ATTR_NOINLINE [[gnu::noinline]] - #define LIBASSERT_UNREACHABLE_CALL __builtin_unreachable() -#else - #define LIBASSERT_PFUNC __FUNCSIG__ - #define LIBASSERT_ATTR_COLD - #define LIBASSERT_ATTR_NOINLINE __declspec(noinline) - #define LIBASSERT_UNREACHABLE_CALL __assume(false) -#endif - -#if LIBASSERT_IS_MSVC - #define LIBASSERT_STRONG_EXPECT(expr, value) (expr) -#elif (defined(__clang__) && __clang_major__ >= 11) || __GNUC__ >= 9 - #define LIBASSERT_STRONG_EXPECT(expr, value) __builtin_expect_with_probability((expr), (value), 1) -#else - #define LIBASSERT_STRONG_EXPECT(expr, value) __builtin_expect((expr), (value)) -#endif - -// deal with gcc shenanigans -// at one point their std::string's move assignment was not noexcept even in c++17 -// https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html -#if defined(_GLIBCXX_USE_CXX11_ABI) - // NOLINTNEXTLINE(misc-include-cleaner) - #define LIBASSERT_GCC_ISNT_STUPID _GLIBCXX_USE_CXX11_ABI -#else - // assume others target new abi by default - homework - #define LIBASSERT_GCC_ISNT_STUPID 1 -#endif - -#if defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL - #define LIBASSERT_NON_CONFORMANT_MSVC_PREPROCESSOR true -#else - #define LIBASSERT_NON_CONFORMANT_MSVC_PREPROCESSOR false -#endif - -#if (LIBASSERT_IS_GCC || LIBASSERT_STD_VER >= 20) && !LIBASSERT_NON_CONFORMANT_MSVC_PREPROCESSOR - // __VA_OPT__ needed for GCC, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=44317 - #define LIBASSERT_VA_ARGS(...) __VA_OPT__(,) __VA_ARGS__ -#else - // clang properly eats the comma with ##__VA_ARGS__ - #define LIBASSERT_VA_ARGS(...) , ##__VA_ARGS__ -#endif - -/// -/// C++20 functionality wrappers. -/// - -// Check if we can use std::is_constant_evaluated. -#ifdef __has_include - #if __has_include() - #include - #ifdef __cpp_lib_is_constant_evaluated - #include - #define LIBASSERT_HAS_IS_CONSTANT_EVALUATED - #endif - #endif -#endif - -// Check if we have the builtin __builtin_is_constant_evaluated. -#ifdef __has_builtin - #if __has_builtin(__builtin_is_constant_evaluated) - #define LIBASSERT_HAS_BUILTIN_IS_CONSTANT_EVALUATED - #endif -#endif - -// GCC 9.1+ and later has __builtin_is_constant_evaluated -#if defined(__GNUC__) && (__GNUC__ >= 9) && !defined(LIBASSERT_HAS_BUILTIN_IS_CONSTANT_EVALUATED) - #define LIBASSERT_HAS_BUILTIN_IS_CONSTANT_EVALUATED -#endif - -// Visual Studio 2019 (19.25) and later supports __builtin_is_constant_evaluated -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 192528326) - #define LIBASSERT_HAS_BUILTIN_IS_CONSTANT_EVALUATED -#endif - -namespace libassert::detail { - // Note: Works with >=C++20 and with C++17 for GCC 9.1+, Clang 9+, and MSVC 19.25+. - constexpr bool is_constant_evaluated() noexcept { - #if defined(LIBASSERT_HAS_IS_CONSTANT_EVALUATED) - return std::is_constant_evaluated(); - #elif defined(LIBASSERT_HAS_BUILTIN_IS_CONSTANT_EVALUATED) - return __builtin_is_constant_evaluated(); - #else - return false; - #endif - } -} - -#if LIBASSERT_IS_CLANG || LIBASSERT_IS_GCC - #if LIBASSERT_IS_GCC - #define LIBASSERT_WARNING_PRAGMA_PUSH_GCC _Pragma("GCC diagnostic push") - #define LIBASSERT_WARNING_PRAGMA_POP_GCC _Pragma("GCC diagnostic pop") - #define LIBASSERT_WARNING_PRAGMA_PUSH_CLANG - #define LIBASSERT_WARNING_PRAGMA_POP_CLANG - #else - #define LIBASSERT_WARNING_PRAGMA_PUSH_GCC - #define LIBASSERT_WARNING_PRAGMA_POP_GCC - #define LIBASSERT_WARNING_PRAGMA_PUSH_CLANG _Pragma("GCC diagnostic push") - #define LIBASSERT_WARNING_PRAGMA_POP_CLANG _Pragma("GCC diagnostic pop") - #endif -#else - #define LIBASSERT_WARNING_PRAGMA_PUSH_CLANG - #define LIBASSERT_WARNING_PRAGMA_POP_CLANG - #define LIBASSERT_WARNING_PRAGMA_PUSH_GCC - #define LIBASSERT_WARNING_PRAGMA_POP_GCC -#endif - -#if LIBASSERT_IS_CLANG || LIBASSERT_IS_ICX - // clang and icx support this as far back as this library could care - #define LIBASSERT_BREAKPOINT() __builtin_debugtrap() -#elif LIBASSERT_IS_MSVC || LIBASSERT_IS_ICC - // msvc and icc support this as far back as this library could care - #define LIBASSERT_BREAKPOINT() __debugbreak() -#elif LIBASSERT_IS_GCC - #if LIBASSERT_GCC_VERSION >= 1200 - #define LIBASSERT_IGNORE_CPP20_EXTENSION_WARNING _Pragma("GCC diagnostic ignored \"-Wc++20-extensions\"") - #else - #define LIBASSERT_IGNORE_CPP20_EXTENSION_WARNING - #endif - #define LIBASSERT_ASM_BREAKPOINT(instruction) \ - do { \ - LIBASSERT_WARNING_PRAGMA_PUSH_GCC \ - LIBASSERT_IGNORE_CPP20_EXTENSION_WARNING \ - __asm__ __volatile__(instruction) \ - ; \ - LIBASSERT_WARNING_PRAGMA_POP_GCC \ - } while(0) - // precedence for these come from llvm's __builtin_debugtrap() implementation - // arm: https://github.com/llvm/llvm-project/blob/e9954ec087d640809082f46d1c7e5ac1767b798d/llvm/lib/Target/ARM/ARMInstrInfo.td#L2393-L2394 - // def : Pat<(debugtrap), (BKPT 0)>, Requires<[IsARM, HasV5T]>; - // def : Pat<(debugtrap), (UDF 254)>, Requires<[IsARM, NoV5T]>; - // thumb: https://github.com/llvm/llvm-project/blob/e9954ec087d640809082f46d1c7e5ac1767b798d/llvm/lib/Target/ARM/ARMInstrThumb.td#L1444-L1445 - // def : Pat<(debugtrap), (tBKPT 0)>, Requires<[IsThumb, HasV5T]>; - // def : Pat<(debugtrap), (tUDF 254)>, Requires<[IsThumb, NoV5T]>; - // aarch64: https://github.com/llvm/llvm-project/blob/e9954ec087d640809082f46d1c7e5ac1767b798d/llvm/lib/Target/AArch64/AArch64FastISel.cpp#L3615-L3618 - // case Intrinsic::debugtrap: - // BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, TII.get(AArch64::BRK)) - // .addImm(0xF000); - // return true; - // x86: https://github.com/llvm/llvm-project/blob/e9954ec087d640809082f46d1c7e5ac1767b798d/llvm/lib/Target/X86/X86InstrSystem.td#L81-L84 - // def : Pat<(debugtrap), - // (INT3)>, Requires<[NotPS]>; - #if defined(__i386__) || defined(__x86_64__) - #define LIBASSERT_BREAKPOINT() LIBASSERT_ASM_BREAKPOINT("int3") - #elif defined(__arm__) || defined(__thumb__) - #if __ARM_ARCH >= 5 - #define LIBASSERT_BREAKPOINT() LIBASSERT_ASM_BREAKPOINT("bkpt #0") - #else - #define LIBASSERT_BREAKPOINT() LIBASSERT_ASM_BREAKPOINT("udf #0xfe") - #endif - #elif defined(__aarch64__) || defined(_M_ARM64) - #define LIBASSERT_BREAKPOINT() LIBASSERT_ASM_BREAKPOINT("brk #0xf000") - #else - // some architecture we aren't prepared for - #define LIBASSERT_BREAKPOINT() - #endif -#else - // some compiler we aren't prepared for - #define LIBASSERT_BREAKPOINT() -#endif - -#endif diff --git a/src/contrib/libassert/stringification.hpp b/src/contrib/libassert/stringification.hpp deleted file mode 100644 index b2b8510..0000000 --- a/src/contrib/libassert/stringification.hpp +++ /dev/null @@ -1,508 +0,0 @@ -#ifndef LIBASSERT_STRINGIFICATION_HPP -#define LIBASSERT_STRINGIFICATION_HPP - -#include -#include -#include -#include - -#include -#include - -#ifdef LIBASSERT_USE_MAGIC_ENUM - // relative include so that multiple library versions don't clash - // e.g. if both libA and libB have different versions of libassert as a public - // dependency, then any library that consumes both will have both sets of include - // paths. this isn't an issue for #include but becomes an issue - // for includes within the library (libA might include from libB) - #if defined(__has_include) && __has_include() - #include - #else - #include - #endif -#endif - -#ifdef LIBASSERT_USE_FMT - #include -#endif - -#if LIBASSERT_STD_VER >= 20 - #include -#endif - -// ===================================================================================================================== -// || Stringification micro-library || -// || Note: There is some stateful stuff behind the scenes related to literal format configuration || -// ===================================================================================================================== - -namespace libassert { - // customization point - template struct stringifier /*{ - std::convertible_to stringify(const T&); - }*/; -} - -namespace libassert::detail { - // What can be stringified - // Base types: - // - anything string-like - // - arithmetic types - // - pointers - // - smart pointers - // - nullptr_t - // - std::error_code/std::error_condition - // - orderings - // - enum values - // User-provided stringifications: - // - std::ostream<< overloads - // - std format TODO - // - fmt TODO - // - libassert::stringifier - // Containers: - // - std::optional - // - std::variant TODO - // - std::expected TODO - // - tuples and tuple-likes - // - anything container-like (std::vector, std::array, std::unordered_map, C arrays, .....) - // Priorities: - // - libassert::stringifier - // - default formatters - // - fmt - // - std fmt TODO - // - osteam format - // - instance of catch all - // Other logistics: - // - Containers are only stringified if their value_type is stringifiable - // TODO Weak pointers? - - template class Ref> - struct is_specialization : std::false_type {}; - - template class Ref, typename... Args> - struct is_specialization, Ref>: std::true_type {}; - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string do_stringify(const T& v); - - namespace stringification { - // - // General traits - // - template class is_tuple_like : public std::false_type {}; - template - class is_tuple_like< - T, - std::void_t< - typename std::tuple_size::type, // TODO: decltype(std::tuple_size_v) ? - decltype(std::get<0>(std::declval())) - > - > : public std::true_type {}; - - namespace adl { - using std::begin, std::end; // ADL - template class is_container : public std::false_type {}; - template - class is_container< - T, - std::void_t< - decltype(begin(decllval())), - decltype(end(decllval())) - > - > : public std::true_type {}; - template class is_begin_deref : public std::false_type {}; - template - class is_begin_deref< - T, - std::void_t< - decltype(*begin(decllval())) - > - > : public std::true_type {}; - } - - template class is_deref : public std::false_type {}; - template - class is_deref< - T, - std::void_t< - decltype(*decllval()) - > - > : public std::true_type {}; - - template class has_ostream_overload : public std::false_type {}; - template - class has_ostream_overload< - T, - std::void_t() << std::declval())> - > : public std::true_type {}; - - template class has_stringifier : public std::false_type {}; - template - class has_stringifier< - T, - std::void_t>{}.stringify(std::declval()))> - > : public std::true_type {}; - - // - // Catch all - // - - template - [[nodiscard]] std::string stringify_unknown() { - return bstringf("", prettify_type(std::string(type_name())).c_str()); - } - - // - // Basic types - // - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::string_view); - // without nullptr_t overload msvc (without /permissive-) will call stringify(bool) and mingw - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::nullptr_t); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(char); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(bool); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(short); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(int); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long long); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned short); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned int); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned long); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned long long); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(float); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(double); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long double); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::error_code ec); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::error_condition ec); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(const std::filesystem::path& path); - #if __cplusplus >= 202002L - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::strong_ordering); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::weak_ordering); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::partial_ordering); - #endif - [[nodiscard]] LIBASSERT_EXPORT - std::string stringify_pointer_value(const void*); - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string stringify_smart_ptr(const T& t) { - if(t) { - return do_stringify(*t); - } else { - return "nullptr"; - } - } - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string stringify_by_ostream(const T& t) { - // clang-tidy bug here - // NOLINTNEXTLINE(misc-const-correctness) - std::ostringstream oss; - oss<>, int> = 0> - LIBASSERT_ATTR_COLD [[nodiscard]] std::string stringify_enum(const T& t) { - std::string_view name = magic_enum::enum_name(t); - if(!name.empty()) { - return std::string(name); - } else { - return bstringf( - "enum %s: %s", - prettify_type(std::string(type_name())).c_str(), - stringify(static_cast::type>(t)).c_str() - ); - } - } - #else - template>, int> = 0> - LIBASSERT_ATTR_COLD [[nodiscard]] std::string stringify_enum(const T& t) { - return bstringf( - "enum %s: %s", - prettify_type(std::string(type_name())).c_str(), - stringify(static_cast>(t)).c_str() - ); - } - #endif - - // - // Compositions of other types - // - // #ifdef __cpp_lib_expected - // template - // [[nodiscard]] std::string stringify(const std::unexpected& x) { - // return "unexpected " + stringify(x.error()); - // } - - // template - // [[nodiscard]] std::string stringify(const std::expected& x) { - // if(x.has_value()) { - // if constexpr(std::is_void_v) { - // return "expected void"; - // } else { - // return "expected " + stringify(*x); - // } - // } else { - // return "unexpected " + stringify(x.error()); - // } - // } - // #endif - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string stringify(const std::optional& t) { - if(t) { - return do_stringify(t.value()); - } else { - return "nullopt"; - } - } - - inline constexpr std::size_t max_container_print_items = 1000; - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string stringify_container(const T& container) { - using std::begin, std::end; // ADL - std::string str = "["; - const auto begin_it = begin(container); - std::size_t count = 0; - for(auto it = begin_it; it != end(container); it++) { - if(it != begin_it) { - str += ", "; - } - str += do_stringify(*it); - if(++count == max_container_print_items) { - str += ", ..."; - break; - } - } - str += "]"; - return str; - } - - // I'm going to assume at least one index because is_tuple_like requires index 0 to exist - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string stringify_tuple_like_impl(const T& t, std::index_sequence) { - return "[" + (do_stringify(std::get<0>(t)) + ... + (", " + do_stringify(std::get(t)))) + "]"; - } - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string stringify_tuple_like(const T& t) { - return stringify_tuple_like_impl(t, std::make_index_sequence::value - 1>{}); - } - } - - template - class has_value_type : public std::false_type {}; - template - class has_value_type< - T, - std::void_t - > : public std::true_type {}; - - template inline constexpr bool is_smart_pointer = - is_specialization::value - || is_specialization::value; // TODO: Handle weak_ptr too? - - template - class can_basic_stringify : public std::false_type {}; - template - class can_basic_stringify< - T, - std::void_t()))> - > : public std::true_type {}; - - template constexpr bool stringifiable_container(); - - template inline constexpr bool stringifiable = - stringification::has_stringifier::value - || std::is_convertible_v - || (std::is_pointer_v || std::is_function_v) - || std::is_enum_v - || (stringification::is_tuple_like::value && stringifiable_container()) - || (stringification::adl::is_container::value && stringifiable_container()) - || can_basic_stringify::value - || stringification::has_ostream_overload::value - #ifdef LIBASSERT_USE_FMT - || fmt::is_formattable::value - #endif - || stringifiable_container(); - - template constexpr bool tuple_has_stringifiable_args_core(std::index_sequence) { - return ( - stringifiable(std::declval()))> - || ... - || stringifiable(std::declval()))> - ); - } - - template inline constexpr bool tuple_has_stringifiable_args = - tuple_has_stringifiable_args_core(std::make_index_sequence::value - 1>{}); - - template constexpr bool stringifiable_container() { - // TODO: Guard against std::expected....? - if constexpr(has_value_type::value) { - if constexpr(std::is_same_v) { // TODO: Reconsider - return false; - } else { - return stringifiable; - } - } else if constexpr(std::is_array_v>) { // C arrays - return stringifiable()[0])>; - } else if constexpr(stringification::is_tuple_like::value) { - return tuple_has_stringifiable_args; - } else { - return false; - } - } - - // Stringification of some types can result in infinite recursion and subsequent stack-overflow. An example would be - // a container/range-like type T whose iterator's value_type is also T (such as std::filesystem::path). This is easy - // enough to detect at compile time, however, there are pathological types in the general case which would be much - // more difficult to check at compile time. - // For example, let T be a container whose iterator's value_type = std::vector. This would cause infinite - // recursion via: - // do_stringify - // -> stringify_container - // -> do_stringify> - // -> stringify_container> - // -> do_stringify - // -> ... - // Instead of compile-time checks this class and a RAII helper is used at the do_stringify level to detect - // stringification recursion at runtime. - class recursion_flag { - bool the_flag = false; - public: - recursion_flag() = default; - recursion_flag(const recursion_flag&) = delete; - recursion_flag(recursion_flag&&) = delete; - recursion_flag& operator=(const recursion_flag&) = delete; - recursion_flag& operator=(recursion_flag&&) = delete; - - class recursion_canary { - bool& flag_ref; - public: - recursion_canary(bool& flag) : flag_ref(flag) {} - ~recursion_canary() { - flag_ref = false; - } - }; - - recursion_canary set() { - the_flag = true; - return recursion_canary(the_flag); - } - - bool test() const { - return the_flag; - } - }; - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string do_stringify(const T& v) { - // TODO: This is overkill to do for every instantiation of do_stringify (e.g. primitive types could omit this) - thread_local recursion_flag flag; - if(flag.test()) { // pathological case detected, fall back to unknown - return stringification::stringify_unknown(); - } - auto canary = flag.set(); - // Ordering notes - // - stringifier first - // - nullptr before string_view (char*) - // - other pointers before basic stringify - // - enum before basic stringify - // - container before basic stringify (c arrays and decay etc) - // - needs to exclude std::filesystem::path - if constexpr(stringification::has_stringifier::value) { - return stringifier>{}.stringify(v); - } else if constexpr(std::is_same_v) { - return "nullptr"; - } else if constexpr(std::is_convertible_v) { - if constexpr(std::is_pointer_v) { - if(v == nullptr) { - return "nullptr"; - } - } - #if LIBASSERT_IS_GCC - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wnonnull" - #endif - return stringification::stringify(std::string_view(v)); - #if LIBASSERT_IS_GCC - #pragma GCC diagnostic pop - #endif - } else if constexpr(std::is_pointer_v || std::is_function_v) { - return stringification::stringify_pointer_value(reinterpret_cast(v)); - } else if constexpr(is_smart_pointer) { - #ifndef LIBASSERT_NO_STRINGIFY_SMART_POINTER_OBJECTS - if(stringifiable) { - #else - if(false) { - #endif - return stringification::stringify_smart_ptr(v); - } else { - return stringification::stringify_pointer_value(v.get()); - } - } else if constexpr(std::is_enum_v) { - return stringification::stringify_enum(v); - } else if constexpr(stringification::is_tuple_like::value) { - if constexpr(stringifiable_container()) { - return stringification::stringify_tuple_like(v); - } else { - return stringification::stringify_unknown(); - } - } else if constexpr( - stringification::adl::is_container::value - && !std::is_same_v, std::filesystem::path> - ) { - if constexpr(stringifiable_container()) { - return stringification::stringify_container(v); - } else { - return stringification::stringify_unknown(); - } - } else if constexpr(can_basic_stringify::value) { - return stringification::stringify(v); - } else if constexpr(stringification::has_ostream_overload::value) { - return stringification::stringify_by_ostream(v); - } - #ifdef LIBASSERT_USE_FMT - else if constexpr(fmt::is_formattable::value) { - return fmt::format("{}", v); - } - #endif - else { - return stringification::stringify_unknown(); - } - // TODO std fmt - } - - // Top-level stringify utility - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string generate_stringification(const T& v) { - if constexpr( - stringification::adl::is_container::value - && !is_string_type - && !std::is_same_v, std::filesystem::path> - && stringifiable_container() - ) { - return prettify_type(std::string(type_name())) + ": " + do_stringify(v); - } else if constexpr(stringification::is_tuple_like::value && stringifiable_container()) { - return prettify_type(std::string(type_name())) + ": " + do_stringify(v); - } else if constexpr((std::is_pointer_v && !is_string_type) || is_smart_pointer) { - return prettify_type(std::string(type_name())) + ": " + do_stringify(v); - } else if constexpr(is_specialization::value) { - return prettify_type(std::string(type_name())) + ": " + do_stringify(v); - } else { - return do_stringify(v); - } - } -} - -#endif diff --git a/src/contrib/libassert/utilities.hpp b/src/contrib/libassert/utilities.hpp deleted file mode 100644 index 746c5f1..0000000 --- a/src/contrib/libassert/utilities.hpp +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef LIBASSERT_UTILITIES_HPP -#define LIBASSERT_UTILITIES_HPP - -#include -#include -#include - -#include - -// ===================================================================================================================== -// || Core utilities || -// ===================================================================================================================== - -namespace libassert { - // Lightweight helper, eventually may use C++20 std::source_location if this library no longer - // targets C++17. Note: __builtin_FUNCTION only returns the name, so __PRETTY_FUNCTION__ is - // still needed. - struct source_location { - const char* file; - //const char* function; // disabled for now due to static constexpr restrictions - int line; - constexpr source_location( - //const char* _function /*= __builtin_FUNCTION()*/, - const char* _file = __builtin_FILE(), - int _line = __builtin_LINE() - ) : file(_file), /*function(_function),*/ line(_line) {} - }; -} - -namespace libassert::detail { - // bootstrap with primitive implementations - LIBASSERT_EXPORT void primitive_assert_impl( - bool condition, - bool normal_assert, - const char* expression, - const char* signature, - source_location location, - const char* message = nullptr - ); - - [[noreturn]] LIBASSERT_EXPORT void primitive_panic_impl ( - const char* signature, - source_location location, - const char* message - ); - - // always_false is just convenient to use here - #define LIBASSERT_PHONY_USE(E) ((void)::libassert::detail::always_false) - - #ifndef NDEBUG - #define LIBASSERT_PRIMITIVE_DEBUG_ASSERT(c, ...) \ - libassert::detail::primitive_assert_impl(c, false, #c, LIBASSERT_PFUNC, {} LIBASSERT_VA_ARGS(__VA_ARGS__)) - #else - #define LIBASSERT_PRIMITIVE_DEBUG_ASSERT(c, ...) LIBASSERT_PHONY_USE(c) - #endif - - #define LIBASSERT_PRIMITIVE_PANIC(message) ::libassert::detail::primitive_panic_impl(LIBASSERT_PFUNC, {}, message) -} - -// ===================================================================================================================== -// || Basic formatting and type tools || -// ===================================================================================================================== - -namespace libassert::detail { - [[nodiscard]] LIBASSERT_EXPORT std::string bstringf(const char* format, ...); - - LIBASSERT_ATTR_COLD [[nodiscard]] - constexpr inline std::string_view substring_bounded_by( - std::string_view sig, - std::string_view l, - std::string_view r - ) noexcept { - auto i = sig.find(l) + l.length(); - return sig.substr(i, sig.rfind(r) - i); - } - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::string_view type_name() noexcept { - // Cases to handle: - // gcc: constexpr std::string_view ns::type_name() [with T = int; std::string_view = std::basic_string_view] - // clang: std::string_view ns::type_name() [T = int] - // msvc: class std::basic_string_view > __cdecl ns::type_name(void) - #if LIBASSERT_IS_CLANG - return substring_bounded_by(LIBASSERT_PFUNC, "[T = ", "]"); - #elif LIBASSERT_IS_GCC - return substring_bounded_by(LIBASSERT_PFUNC, "[with T = ", "; std::string_view = "); - #elif LIBASSERT_IS_MSVC - return substring_bounded_by(LIBASSERT_PFUNC, "type_name<", ">(void)"); - #else - return LIBASSERT_PFUNC; - #endif - } - - [[nodiscard]] LIBASSERT_EXPORT std::string prettify_type(std::string type); -} - -// ===================================================================================================================== -// || Metaprogramming utilities || -// ===================================================================================================================== - -namespace libassert::detail { - struct nothing {}; - - template inline constexpr bool is_nothing = std::is_same_v; - - // Hack to get around static_assert(false); being evaluated before any instantiation, even under - // an if-constexpr branch - // Also used for PHONY_USE - template inline constexpr bool always_false = false; - - template using strip = std::remove_cv_t>; - - // intentionally not stripping B - template inline constexpr bool isa = std::is_same_v, B>; - - // Is integral but not boolean - template inline constexpr bool is_integral_and_not_bool = std::is_integral_v> && !isa; - - template - inline constexpr bool is_arith_not_bool_char = std::is_arithmetic_v> && !isa && !isa; - - template - inline constexpr bool is_c_string = - isa>, char*> // <- covers literals (i.e. const char(&)[N]) too - || isa>, const char*>; - - template - inline constexpr bool is_string_type = - isa - || isa - || is_c_string; - - // char(&)[20], const char(&)[20], const char(&)[] - template inline constexpr bool is_string_literal = - std::is_lvalue_reference_v - && std::is_array_v> - && isa>, char>; - - template typename std::add_lvalue_reference_t decllval() noexcept; -} - -#endif diff --git a/src/main.cpp b/src/main.cpp index 49f2a87..b19e33b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,7 +15,7 @@ #include #include #include - +#include namespace dropshell { @@ -111,7 +111,7 @@ int main(int argc, char* argv[]) { HAPPYEXIT("makesafecmd", std::cout<=3, "Error: logic error!"); + ASSERT(argc>=3, "Error: logic error!"); service_runner::edit_server(safearg(argc,argv,2)); return 0; } diff --git a/src/server_env_manager.cpp b/src/server_env_manager.cpp index f299ba9..b9ec6a8 100644 --- a/src/server_env_manager.cpp +++ b/src/server_env_manager.cpp @@ -166,7 +166,7 @@ bool server_env_manager::run_remote_template_command(const std::string &service_ sCommand scommand = construct_standard_template_run_cmd(service_name, command, args, silent); if (scommand.get_command_to_run().empty()) return false; - cMode mode = (command=="ssh") ? cMode::Interactive : cMode::Silent; + cMode mode = (command=="ssh") ? (cMode::Interactive | cMode::RawCommand) : cMode::Silent; return execute_ssh_command(get_SSH_INFO(), scommand, mode); } diff --git a/src/service_runner.cpp b/src/service_runner.cpp index c597d0b..ea0d7ec 100644 --- a/src/service_runner.cpp +++ b/src/service_runner.cpp @@ -1,10 +1,4 @@ -#include "config.hpp" -#include "service_runner.hpp" -#include "server_env_manager.hpp" -#include "templates.hpp" -#include "services.hpp" -#include "utils/directories.hpp" -#include "utils/utils.hpp" + #include #include #include @@ -13,6 +7,16 @@ #include #include #include +#include + +#include "config.hpp" +#include "service_runner.hpp" +#include "server_env_manager.hpp" +#include "templates.hpp" +#include "services.hpp" +#include "utils/directories.hpp" +#include "utils/utils.hpp" + namespace fs = std::filesystem; @@ -413,7 +417,7 @@ bool service_runner::interactive_ssh(const std::string & server_name, const std: std::cerr << "Error: Invalid server environment file: " << server_name << std::endl; return false; } - return execute_ssh_command(env.get_SSH_INFO(), scommand, cMode::Interactive); + return execute_ssh_command(env.get_SSH_INFO(), scommand, cMode::Interactive | cMode::RawCommand); } void service_runner::edit_server(const std::string &server_name) @@ -461,7 +465,7 @@ bool service_runner::edit_file(const std::string &file_path) } std::cout << "Editing file: " << file_path << std::endl; - return execute_local_command(editor_cmd, cMode::Interactive); + return execute_local_command(editor_cmd, cMode::Interactive | cMode::RawCommand); } bool service_runner::interactive_ssh_service() diff --git a/src/service_runner.hpp b/src/service_runner.hpp index af3fd3f..d21ddb9 100644 --- a/src/service_runner.hpp +++ b/src/service_runner.hpp @@ -12,7 +12,6 @@ #include "server_env_manager.hpp" #include "services.hpp" #include "utils/utils.hpp" -#include "utils/assert.hpp" #include "utils/hash.hpp" namespace dropshell { diff --git a/src/templates.cpp b/src/templates.cpp index 1a94f5e..bcdffd4 100644 --- a/src/templates.cpp +++ b/src/templates.cpp @@ -6,13 +6,13 @@ #include #include #include + #include #include "utils/envmanager.hpp" #include "utils/directories.hpp" #include "utils/utils.hpp" #include "templates.hpp" #include "config.hpp" - #include "utils/assert.hpp" namespace dropshell { diff --git a/src/utils/assert.hpp b/src/utils/assert.hpp deleted file mode 100644 index 7c22d57..0000000 --- a/src/utils/assert.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef DROPSHELL_ASSERT_HPP -#define DROPSHELL_ASSERT_HPP - -#include -#include -#include // For std::exit and EXIT_FAILURE -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ds { - -struct SourceLocation { - const char* file_name; - int line; - const char* function_name; -}; - -// Helper macro to create a SourceLocation with current context -#define DS_CURRENT_LOCATION ds::SourceLocation{__FILE__, __LINE__, __func__} - -inline uintptr_t get_base_address(const char* exe_path) { - // Open /proc/self/maps - FILE* maps = fopen("/proc/self/maps", "r"); - if (!maps) return 0; - char line[512]; - uintptr_t base_addr = 0; - while (fgets(line, sizeof(line), maps)) { - // Look for a line containing the exe path and 'r-xp' - if (strstr(line, exe_path) && strstr(line, "r-xp")) { - // Parse the start address - char* endptr = nullptr; - base_addr = strtoull(line, &endptr, 16); - break; - } - } - fclose(maps); - return base_addr; -} - -inline void print_stacktrace() { - constexpr int max_frames = 64; - void* addrlist[max_frames]; - int addrlen = backtrace(addrlist, max_frames); - - if (addrlen == 0) { - std::cerr << " \n"; - return; - } - - // Get the program name - char exe_path[1024]; - ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); - if (len == -1) { - std::cerr << " \n"; - return; - } - exe_path[len] = '\0'; - - uintptr_t base_addr = get_base_address(exe_path); - - int frame_num = 1; - for (int i = 0; i < addrlen; i++) { - uintptr_t addr = (uintptr_t)addrlist[i]; - uintptr_t offset = base_addr ? (addr - base_addr) : addr; - std::ostringstream cmd; - cmd << "addr2line -e " << exe_path << " -f -C 0x" << std::hex << offset; - std::unique_ptr pipe(popen(cmd.str().c_str(), "r"), pclose); - if (!pipe) { - std::cerr << " \n\n"; - frame_num++; - continue; - } - // addr2line -f prints two lines per address: function name, then file:line - char func[256] = {0}; - char fileline[256] = {0}; - if (fgets(func, sizeof(func), pipe.get()) && fgets(fileline, sizeof(fileline), pipe.get())) { - // Remove trailing newlines - size_t len1 = strlen(func); - if (len1 > 0 && func[len1-1] == '\n') func[len1-1] = 0; - size_t len2 = strlen(fileline); - if (len2 > 0 && fileline[len2-1] == '\n') fileline[len2-1] = 0; - // Remove argument list from function name (truncate at first '(') - char* paren = strchr(func, '('); - if (paren) *paren = 0; - std::cerr << " [" << frame_num << "] " << func << " at " << fileline << "\n\n"; - } - frame_num++; - } -} - -[[noreturn]] inline void assert_fail( - const char* expression, - const SourceLocation& location, - const char* message = nullptr) { - - std::cerr << "\033[1;31mAssertion failed!\033[0m\n" - << "Expression: \033[1;33m" << expression << "\033[0m\n" - << "Location: \033[1;36m" << location.file_name << ":" - << location.line << "\033[0m\n" - << "Function: \033[1;36m" << location.function_name << "\033[0m\n"; - - if (message) { - std::cerr << "Message: \033[1;37m" << message << "\033[0m\n"; - } - - std::cerr << std::endl; - - std::cerr << "Stack trace:" << std::endl; - print_stacktrace(); - std::cerr << "----------------------------------------" << std::endl; - - // Exit the program without creating a core dump - std::exit(EXIT_FAILURE); -} - -} // namespace ds - -// Standard assertion -#define ASSERT(condition) \ - do { \ - if (!(condition)) { \ - ds::assert_fail(#condition, DS_CURRENT_LOCATION); \ - } \ - } while (false) - -// Assertion with custom message -#define ASSERT_MSG(condition, message) \ - do { \ - if (!(condition)) { \ - ds::assert_fail(#condition, DS_CURRENT_LOCATION, message); \ - } \ - } while (false) - -#endif // DROPSHELL_ASSERT_HPP diff --git a/src/utils/execute.cpp b/src/utils/execute.cpp index fe081af..b4a7c7e 100644 --- a/src/utils/execute.cpp +++ b/src/utils/execute.cpp @@ -1,8 +1,3 @@ -#include "execute.hpp" -#include "assert.hpp" -#include "contrib/base64.hpp" -#include "utils/utils.hpp" - #include #include #include @@ -11,6 +6,12 @@ #include #include #include +#include + +#include "execute.hpp" +#include "contrib/base64.hpp" +#include "utils/utils.hpp" + bool EXITSTATUSCHECK(int ret) { return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute. @@ -55,9 +56,9 @@ bool execute_local_command_interactive(const sCommand &command, bool silent) bool execute_local_command_and_capture_output(const sCommand& command, std::string * output, cMode mode) { - ASSERT_MSG(output != nullptr, "Output string must be provided"); - ASSERT_MSG(is_raw(mode), "Capture output mode requires raw command mode"); - ASSERT_MSG(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode"); + ASSERT(output != nullptr, "Output string must be provided"); + ASSERT(is_raw(mode), "Capture output mode requires raw command mode"); + ASSERT(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode"); if (command.get_command_to_run().empty()) return false; cStyle style = getStyle(mode); @@ -79,18 +80,18 @@ bool execute_local_command_and_capture_output(const sCommand& command, std::stri bool execute_local_command(const sCommand & command, cMode mode, std::string * output /* = nullptr */) { if (hasFlag(mode, cMode::Interactive)) { - ASSERT_MSG(! hasFlag(mode, cMode::CaptureOutput), "Interactive mode and capture output mode cannot be used together"); - ASSERT_MSG(output == nullptr, "Interactive mode and an output string cannot be used together"); - ASSERT_MSG(is_raw(mode), "Interactive mode requires raw command mode"); + ASSERT(! hasFlag(mode, cMode::CaptureOutput), "Interactive mode and capture output mode cannot be used together"); + ASSERT(output == nullptr, "Interactive mode and an output string cannot be used together"); + ASSERT(is_raw(mode), "Interactive mode requires raw command mode"); return execute_local_command_interactive(command, hasFlag(mode, cMode::Silent)); } if (hasFlag(mode, cMode::CaptureOutput)) { - ASSERT_MSG(output != nullptr, "Capture output mode requires an output string to be provided"); - ASSERT_MSG(is_raw(mode), "Capture output mode requires raw command mode"); - ASSERT_MSG(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode"); - + ASSERT(output != nullptr, "Capture output mode requires an output string to be provided"); + ASSERT(is_raw(mode), "Capture output mode requires raw command mode"); + ASSERT(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode"); + return execute_local_command_and_capture_output(command, output, mode); } @@ -113,8 +114,8 @@ bool execute_ssh_command(const sSSHInfo &ssh_info, const sCommand &command, cMod if (command.get_command_to_run().empty()) return false; - ASSERT_MSG(!(hasFlag(mode, cMode::Interactive) && !is_raw(mode)), "Interactive mode requires raw command mode"); - ASSERT_MSG(!(hasFlag(mode, cMode::CaptureOutput) && output == nullptr), "Capture output mode must be used with an output string"); + ASSERT(!(hasFlag(mode, cMode::Interactive) && !is_raw(mode)), "Interactive mode requires raw command mode"); + ASSERT(!(hasFlag(mode, cMode::CaptureOutput) && output == nullptr), "Capture output mode must be used with an output string"); std::stringstream ssh_cmd; ssh_cmd << "ssh -p " << ssh_info.port << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "") @@ -126,9 +127,8 @@ bool execute_ssh_command(const sSSHInfo &ssh_info, const sCommand &command, cMod else { std::string raw_cmd = command.construct_cmd(cStyle::Raw); - ASSERT_MSG(raw_cmd.find("'") == std::string::npos, "Raw command must not contain single quotes"); - ASSERT_MSG(raw_cmd.find("\"") == std::string::npos, "Raw command must not contain double quotes"); - cmdstr = halfquote("bash -c " + quote(raw_cmd)); + ASSERT(raw_cmd.find("'") == std::string::npos, "Raw command must not contain single quotes"); + cmdstr = "bash -c "+ halfquote(raw_cmd); } sCommand ssh_command(ssh_cmd.str() + " " + cmdstr);