Thank you for the very useful library! Few improvements: - Better header hygiene - Isolate `windows.h` to `.cpp` whenever possible - Use `WIN32_LEAN_AND_MEAN` - Remove unused headers Tested on Windows with ``` cmake .. -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS="-ftime-trace -Wall -Wextra -Wpedantic -Wno-ignored-attributes" -DCMAKE_COLOR_DIAGNOSTICS=1 -DCPPTRACE_BUILD_TESTING=1 -DCPPTRACE_BUILD_BENCHMARKING=0 ``` There's a lot more that can be improved if you are interested. --------- Co-authored-by: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com>
171 lines
4.9 KiB
C++
171 lines
4.9 KiB
C++
#ifndef ERROR_HPP
|
|
#define ERROR_HPP
|
|
|
|
#include <exception>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "platform/platform.hpp"
|
|
#include "utils/microfmt.hpp"
|
|
|
|
#if IS_MSVC
|
|
#define CPPTRACE_PFUNC __FUNCSIG__
|
|
#else
|
|
#define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__
|
|
#endif
|
|
|
|
namespace cpptrace {
|
|
namespace detail {
|
|
class internal_error : public std::exception {
|
|
std::string msg;
|
|
public:
|
|
internal_error(std::string message) : msg("Cpptrace internal error: " + std::move(message)) {}
|
|
template<typename... Args>
|
|
internal_error(const char* format, Args&&... args) : internal_error(microfmt::format(format, args...)) {}
|
|
const char* what() const noexcept override {
|
|
return msg.c_str();
|
|
}
|
|
};
|
|
|
|
// Lightweight std::source_location.
|
|
struct source_location {
|
|
const char* const file;
|
|
const int line;
|
|
constexpr source_location(
|
|
const char* _file,
|
|
int _line
|
|
) : file(_file), line(_line) {}
|
|
};
|
|
|
|
#define CPPTRACE_CURRENT_LOCATION ::cpptrace::detail::source_location(__FILE__, __LINE__)
|
|
|
|
enum class assert_type {
|
|
assert,
|
|
verify,
|
|
panic,
|
|
};
|
|
|
|
constexpr const char* assert_actions[] = {"assertion", "verification", "panic"};
|
|
constexpr const char* assert_names[] = {"ASSERT", "VERIFY", "PANIC"};
|
|
|
|
[[noreturn]] inline void assert_fail(
|
|
assert_type type,
|
|
const char* expression,
|
|
const char* signature,
|
|
source_location location,
|
|
const char* message
|
|
) {
|
|
const char* action = assert_actions[static_cast<std::underlying_type<assert_type>::type>(type)];
|
|
const char* name = assert_names[static_cast<std::underlying_type<assert_type>::type>(type)];
|
|
if(message == nullptr) {
|
|
throw internal_error(
|
|
"Cpptrace {} failed at {}:{}: {}\n"
|
|
" {}({});\n",
|
|
action, location.file, location.line, signature,
|
|
name, expression
|
|
);
|
|
} else {
|
|
throw internal_error(
|
|
"Cpptrace {} failed at {}:{}: {}: {}\n"
|
|
" {}({});\n",
|
|
action, location.file, location.line, signature, message,
|
|
name, expression
|
|
);
|
|
}
|
|
}
|
|
|
|
[[noreturn]] inline void panic(
|
|
const char* signature,
|
|
source_location location,
|
|
const std::string& message = ""
|
|
) {
|
|
if(message == "") {
|
|
throw internal_error(
|
|
"Cpptrace panic {}:{}: {}\n",
|
|
location.file, location.line, signature
|
|
);
|
|
} else {
|
|
throw internal_error(
|
|
"Cpptrace panic {}:{}: {}: {}\n",
|
|
location.file, location.line, signature, message.c_str()
|
|
);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void nullfn() {
|
|
// this method doesn't do anything and is never called.
|
|
}
|
|
|
|
#define PHONY_USE(...) (nullfn<decltype(__VA_ARGS__)>())
|
|
|
|
// Work around a compiler warning
|
|
template<typename T>
|
|
bool as_bool(T&& value) {
|
|
return static_cast<bool>(std::forward<T>(value));
|
|
}
|
|
|
|
// Work around a compiler warning
|
|
template<typename T>
|
|
std::string as_string(T&& value) {
|
|
return std::string(std::forward<T>(value));
|
|
}
|
|
|
|
inline std::string as_string() {
|
|
return "";
|
|
}
|
|
|
|
// Check condition in both debug and release. std::runtime_error on failure.
|
|
#define PANIC(...) ((::cpptrace::detail::panic)(CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION, ::cpptrace::detail::as_string(__VA_ARGS__)))
|
|
|
|
template<typename T>
|
|
void assert_impl(
|
|
T condition,
|
|
const char* message,
|
|
assert_type type,
|
|
const char* args,
|
|
const char* signature,
|
|
source_location location
|
|
) {
|
|
if(!as_bool(condition)) {
|
|
assert_fail(type, args, signature, location, message);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void assert_impl(
|
|
T condition,
|
|
assert_type type,
|
|
const char* args,
|
|
const char* signature,
|
|
source_location location
|
|
) {
|
|
assert_impl(
|
|
condition,
|
|
nullptr,
|
|
type,
|
|
args,
|
|
signature,
|
|
location
|
|
);
|
|
}
|
|
|
|
// Check condition in both debug and release. std::runtime_error on failure.
|
|
#define VERIFY(...) ( \
|
|
assert_impl(__VA_ARGS__, ::cpptrace::detail::assert_type::verify, #__VA_ARGS__, CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION) \
|
|
)
|
|
|
|
#ifndef NDEBUG
|
|
// Check condition in both debug. std::runtime_error on failure.
|
|
#define ASSERT(...) ( \
|
|
assert_impl(__VA_ARGS__, ::cpptrace::detail::assert_type::assert, #__VA_ARGS__, CPPTRACE_PFUNC, CPPTRACE_CURRENT_LOCATION) \
|
|
)
|
|
#else
|
|
// Check condition in both debug. std::runtime_error on failure.
|
|
#define ASSERT(...) PHONY_USE(__VA_ARGS__)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#endif
|