diff --git a/src/Ensure.hpp b/src/Ensure.hpp index b29baea..dca3240 100644 --- a/src/Ensure.hpp +++ b/src/Ensure.hpp @@ -1,19 +1,16 @@ /** @file */ -#include "Exception.hpp" -#include +#include "ErrorHelpers.hpp" /// \defgroup Ensure Ensure /// \brief A set of ensure macros. #ifndef NO_ENSURE + /// @brief Ensures an expression is true. Throws an exception if the assertion fails. /// @ingroup Ensure #define Ensure(expr) \ if (!(expr)) { \ - std::stringstream ss; \ - ss << "[" << file_name(__FILE__) << ":" << __LINE__ << "] ENSURE FAILED: \""; \ - ss << #expr << "\""; \ - throw ArbUt::Exception(ss.str()); \ + throw ArbUt::__ErrorHelpers::CreateEnsureError(#expr, std::source_location::current()); \ } /// @brief Ensures an expression is true for a range. The value in the range can be retrieved using ``item``. @@ -32,6 +29,7 @@ /// @brief Ensures a pointer is not null. /// @ingroup Ensure #define EnsureNotNull(value) Ensure((value) != nullptr) + /// @brief Ensures a range is not null. /// @ingroup Ensure #define EnsureAllNotNull(iterator) EnsureForEach(iterator, item != nullptr) diff --git a/src/ErrorHelpers.cpp b/src/ErrorHelpers.cpp new file mode 100644 index 0000000..aeb28eb --- /dev/null +++ b/src/ErrorHelpers.cpp @@ -0,0 +1,18 @@ +#include "ErrorHelpers.hpp" + +namespace ArbUt { + Exception __ErrorHelpers::CreateEnsureError(const char* exp, const std::source_location& location) { + std::stringstream error; + error << "[" << file_name(location.file_name()) << ":" << location.line() << "] ENSURE FAILED: \""; + error << exp << "\""; + return Exception(error.str()); + } + + ArbUt::Exception __ErrorHelpers::CreateThrowError(const char* exp, const std::source_location& location) { + std::stringstream error; + error << "[" << file_name(location.file_name()) << ":" << location.line() << "] \""; + error << exp; + error << "\""; + return Exception(error.str()); + } +} \ No newline at end of file diff --git a/src/ErrorHelpers.hpp b/src/ErrorHelpers.hpp new file mode 100644 index 0000000..116660e --- /dev/null +++ b/src/ErrorHelpers.hpp @@ -0,0 +1,41 @@ +#ifndef ARBUTILS_ERRORHELPERS_HPP +#define ARBUTILS_ERRORHELPERS_HPP +#if defined(__clang__) +#include +#else +#include +#endif +#include +#include "Exception.hpp" + +#if defined(__clang__) +namespace std { + using source_location = std::experimental::source_location; +} +#endif + +namespace ArbUt { + class Exception; + + /// @brief Helper class for multiple error handling + class __ErrorHelpers { + static constexpr const char* file_name(const char* path) { + const char* file = path; + while (*path != 0) { + if (*path++ == '/') { + file = path; + } + } + return file; + } + + public: + /// @brief Create an error for the Ensure macro + static ArbUt::Exception CreateEnsureError(const char* exp, const std::source_location& location); + + /// @brief Create an error for the Throw macro + static ArbUt::Exception CreateThrowError(const char* exp, const std::source_location& location); + }; +}; + +#endif // ARBUTILS_ERRORHELPERS_HPP diff --git a/src/Exception.hpp b/src/Exception.hpp index 5e55b56..2368b25 100644 --- a/src/Exception.hpp +++ b/src/Exception.hpp @@ -1,7 +1,13 @@ #ifndef ARBUTILS_EXCEPTION_HPP #define ARBUTILS_EXCEPTION_HPP -#include #include +#include +#include "ErrorHelpers.hpp" +#if defined(__clang__) +#include +#else +#include +#endif #if !WINDOWS #if PRETTYTRACES @@ -10,6 +16,22 @@ #include "../extern/backward.hpp" #endif +#if defined(__clang__) +namespace std { + using source_location = std::experimental::source_location; +} +#endif + +static constexpr const char* file_name(const char* path) { + const char* file = path; + while (*path != 0) { + if (*path++ == '/') { + file = path; + } + } + return file; +} + namespace ArbUt { /// @brief Implementation of std::logic_error that gives a stack trace when thrown. class Exception : std::logic_error { @@ -95,6 +117,17 @@ namespace ArbUt { return ss.str(); } + template + static void Throw(const std::string& expression, const std::source_location& location, const Args... args) { + std::stringstream error; + error << "[" << file_name(location.file_name()) << ":" << location.line() << "] \""; + error << expression; + using List = int[]; + (void)List{0, ((void)(error << args), 0)...}; + error << "\""; + throw Exception(error.str()); + } + private: static void AppendNoSourceStack(std::stringstream& ss, const backward::ResolvedTrace& trace, bool include_addr) { @@ -143,22 +176,8 @@ namespace ArbUt { #define consteval constexpr #endif -static consteval const char* file_name(const char* path) { - const char* file = path; - while (*path != 0) { - if (*path++ == '/') { - file = path; - } - } - return file; -} - -#define THROW(message) \ - { \ - std::stringstream ___ss; \ - ___ss << "[" << file_name(__FILE__) << ":" << __LINE__ << "] " << message; \ - throw ArbUt::Exception(___ss.str()); \ - } +#define THROW(message, ...) \ + { ArbUt::Exception::Throw(message, std::source_location::current(), ##__VA_ARGS__); } #define NOT_REACHABLE THROW("Not reachable"); #define NOT_IMPLEMENTED THROW("Not implemented"); diff --git a/tests/ExceptionTests.cpp b/tests/ExceptionTests.cpp index 03f08d7..efd4dec 100644 --- a/tests/ExceptionTests.cpp +++ b/tests/ExceptionTests.cpp @@ -4,6 +4,8 @@ using namespace ArbUt; static void Thrower() { throw ArbUt::Exception("foobar"); } +static void ThrowerWithMacro() { THROW("foobar"); } +static void ThrowerWithMacroAndParameters([[maybe_unused]] int i) { THROW("foobar", ' ', i); } TEST_CASE("Throw exception get stack trace") { try { @@ -16,4 +18,20 @@ TEST_CASE("Throw exception get stack trace") { } } +TEST_CASE("Throw exception with macro, check message") { + try { + ThrowerWithMacro(); + } catch (const ArbUt::Exception& e) { + REQUIRE_EQ(std::string(e.what()), "[ExceptionTests.cpp:7] \"foobar\""); + } +} + +TEST_CASE("Throw exception with macro and parameters, check message") { + try { + ThrowerWithMacroAndParameters(684); + } catch (const ArbUt::Exception& e) { + REQUIRE_EQ(std::string(e.what()), "[ExceptionTests.cpp:8] \"foobar 684\""); + } +} + #endif \ No newline at end of file