Reworks ensure and throw to not fully depend on macros anymore.
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2021-11-21 12:27:46 +01:00
parent ea5c824d34
commit 1be055760a
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
5 changed files with 117 additions and 23 deletions

View File

@ -1,19 +1,16 @@
/** @file */ /** @file */
#include "Exception.hpp" #include "ErrorHelpers.hpp"
#include <sstream>
/// \defgroup Ensure Ensure /// \defgroup Ensure Ensure
/// \brief A set of ensure macros. /// \brief A set of ensure macros.
#ifndef NO_ENSURE #ifndef NO_ENSURE
/// @brief Ensures an expression is true. Throws an exception if the assertion fails. /// @brief Ensures an expression is true. Throws an exception if the assertion fails.
/// @ingroup Ensure /// @ingroup Ensure
#define Ensure(expr) \ #define Ensure(expr) \
if (!(expr)) { \ if (!(expr)) { \
std::stringstream ss; \ throw ArbUt::__ErrorHelpers::CreateEnsureError(#expr, std::source_location::current()); \
ss << "[" << file_name(__FILE__) << ":" << __LINE__ << "] ENSURE FAILED: \""; \
ss << #expr << "\""; \
throw ArbUt::Exception(ss.str()); \
} }
/// @brief Ensures an expression is true for a range. The value in the range can be retrieved using ``item``. /// @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. /// @brief Ensures a pointer is not null.
/// @ingroup Ensure /// @ingroup Ensure
#define EnsureNotNull(value) Ensure((value) != nullptr) #define EnsureNotNull(value) Ensure((value) != nullptr)
/// @brief Ensures a range is not null. /// @brief Ensures a range is not null.
/// @ingroup Ensure /// @ingroup Ensure
#define EnsureAllNotNull(iterator) EnsureForEach(iterator, item != nullptr) #define EnsureAllNotNull(iterator) EnsureForEach(iterator, item != nullptr)

18
src/ErrorHelpers.cpp Normal file
View File

@ -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());
}
}

41
src/ErrorHelpers.hpp Normal file
View File

@ -0,0 +1,41 @@
#ifndef ARBUTILS_ERRORHELPERS_HPP
#define ARBUTILS_ERRORHELPERS_HPP
#if defined(__clang__)
#include <experimental/source_location>
#else
#include <source_location>
#endif
#include <sstream>
#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

View File

@ -1,7 +1,13 @@
#ifndef ARBUTILS_EXCEPTION_HPP #ifndef ARBUTILS_EXCEPTION_HPP
#define ARBUTILS_EXCEPTION_HPP #define ARBUTILS_EXCEPTION_HPP
#include <string>
#include <stdexcept> #include <stdexcept>
#include <string>
#include "ErrorHelpers.hpp"
#if defined(__clang__)
#include <experimental/source_location>
#else
#include <source_location>
#endif
#if !WINDOWS #if !WINDOWS
#if PRETTYTRACES #if PRETTYTRACES
@ -10,6 +16,22 @@
#include "../extern/backward.hpp" #include "../extern/backward.hpp"
#endif #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 { namespace ArbUt {
/// @brief Implementation of std::logic_error that gives a stack trace when thrown. /// @brief Implementation of std::logic_error that gives a stack trace when thrown.
class Exception : std::logic_error { class Exception : std::logic_error {
@ -95,6 +117,17 @@ namespace ArbUt {
return ss.str(); return ss.str();
} }
template <typename... Args>
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: private:
static void AppendNoSourceStack(std::stringstream& ss, const backward::ResolvedTrace& trace, static void AppendNoSourceStack(std::stringstream& ss, const backward::ResolvedTrace& trace,
bool include_addr) { bool include_addr) {
@ -143,22 +176,8 @@ namespace ArbUt {
#define consteval constexpr #define consteval constexpr
#endif #endif
static consteval const char* file_name(const char* path) { #define THROW(message, ...) \
const char* file = path; { ArbUt::Exception::Throw(message, std::source_location::current(), ##__VA_ARGS__); }
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 NOT_REACHABLE THROW("Not reachable"); #define NOT_REACHABLE THROW("Not reachable");
#define NOT_IMPLEMENTED THROW("Not implemented"); #define NOT_IMPLEMENTED THROW("Not implemented");

View File

@ -4,6 +4,8 @@
using namespace ArbUt; using namespace ArbUt;
static void Thrower() { throw ArbUt::Exception("foobar"); } 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") { TEST_CASE("Throw exception get stack trace") {
try { 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 #endif