From 3bf1bc6f7372e0d4e49f2d3100f6a2fd39a6a451 Mon Sep 17 00:00:00 2001 From: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:18:08 -0600 Subject: [PATCH] Make the default failure handler public and update docs for custom failure handlers --- README.md | 19 ++++++++++++--- include/libassert/assert.hpp | 2 ++ src/assert.cpp | 47 ++++++++++++++++++------------------ 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index c21aaa4..2b3be38 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ screenshots above. This is to help enhance readability. Libassert supports custom assertion failure handlers: ```cpp -void handler(assert_type type, const assertion_info& assertion) { +void handler(const assertion_info& info) { throw std::runtime_error("Assertion failed:\n" + assertion.to_string()); } @@ -224,6 +224,8 @@ int main() { } ``` +More details [below](#custom-failure-handlers-1). + ## Debug Stringification A lot of care is given to producing debug stringifications of values as effectively as possible: Strings, characters, @@ -689,15 +691,26 @@ Lastly, any types with an ostream `operator<<` overload can be stringified. ```cpp namespace libassert { void set_failure_handler(void (*handler)(const assertion_info&)); + [[noreturn]] void default_failure_handler(const assertion_info& info); } ``` - `set_failure_handler`: Sets the assertion handler for the program. +- `default_failure_handler`: The default failure handler, provided for convenience. + +Example: If you wanted to log to a file in addition to the default behavior you could do something along the lines of: + +```cpp +void handler(const assertion_info& info) { + logger::error(info.to_string()); + libassert::default_failure_handler(info); +} +``` -An example assertion handler similar to the default handler: +For more complex custom handling you can modify the default handler's logic: ```cpp -void libassert_default_failure_handler(const assertion_info& info) { +void default_failure_handler(const assertion_info& info) { libassert::enable_virtual_terminal_processing_if_needed(); // for terminal colors on windows std::string message = info.to_string( libassert::terminal_width(libassert::stderr_fileno), diff --git a/include/libassert/assert.hpp b/include/libassert/assert.hpp index b11296d..fe9bf09 100644 --- a/include/libassert/assert.hpp +++ b/include/libassert/assert.hpp @@ -165,6 +165,8 @@ namespace libassert { struct assertion_info; + LIBASSERT_EXPORT [[noreturn]] void default_failure_handler(const assertion_info& info); + LIBASSERT_EXPORT void set_failure_handler(void (*handler)(const assertion_info&)); struct LIBASSERT_EXPORT binary_diagnostics_descriptor { diff --git a/src/assert.cpp b/src/assert.cpp index 75ec5d0..1ea41f3 100644 --- a/src/assert.cpp +++ b/src/assert.cpp @@ -422,32 +422,33 @@ namespace libassert { } } - LIBASSERT_ATTR_COLD - void libassert_default_failure_handler(const assertion_info& info) { - enable_virtual_terminal_processing_if_needed(); // for terminal colors on windows - std::string message = info.to_string( - terminal_width(STDERR_FILENO), - isatty(STDERR_FILENO) ? get_color_scheme() : color_scheme::blank - ); - std::cerr << message << std::endl; - switch(info.type) { - case assert_type::assertion: - case assert_type::debug_assertion: - case assert_type::assumption: - case assert_type::panic: - case assert_type::unreachable: - (void)fflush(stderr); - std::abort(); - // Breaking here as debug CRT allows aborts to be ignored, if someone wants to make a debug build of - // this library - break; - default: - LIBASSERT_PRIMITIVE_DEBUG_ASSERT(false); - } + } + + LIBASSERT_ATTR_COLD LIBASSERT_EXPORT [[noreturn]] + void default_failure_handler(const assertion_info& info) { + enable_virtual_terminal_processing_if_needed(); // for terminal colors on windows + std::string message = info.to_string( + terminal_width(STDERR_FILENO), + isatty(STDERR_FILENO) ? get_color_scheme() : color_scheme::blank + ); + std::cerr << message << std::endl; + switch(info.type) { + case assert_type::assertion: + case assert_type::debug_assertion: + case assert_type::assumption: + case assert_type::panic: + case assert_type::unreachable: + (void)fflush(stderr); + std::abort(); + // Breaking here as debug CRT allows aborts to be ignored, if someone wants to make a debug build of + // this library + break; + default: + LIBASSERT_PRIMITIVE_PANIC("Unknown assertion type in assertion failure handler"); } } - static std::atomic failure_handler = detail::libassert_default_failure_handler; + static std::atomic failure_handler = default_failure_handler; LIBASSERT_ATTR_COLD LIBASSERT_EXPORT void set_failure_handler(void (*handler)(const assertion_info&)) {