NBEngine/include/Errors.hpp
2025-11-09 23:36:12 -06:00

138 lines
3.9 KiB
C++

#pragma once
#ifndef _NB_ERROR
#define _NB_ERROR
#include <exception>
#include <memory>
#include <string>
#include <type_traits>
#include <unordered_map>
namespace nb {
typedef std::unordered_map<unsigned int, const char*> ErrorCodeMap;
template<class ErrorType>
class ErrorBase : public std::exception {
public:
static std::string lookup(unsigned int);
unsigned int code() const noexcept {
return static_cast<const ErrorType*>(this)->_code;
};
virtual const char* what() const noexcept override final { return _msg.c_str(); };
protected:
ErrorBase() = default;
ErrorBase(
const unsigned int code,
std::string msg,
unsigned int line=0,
std::string filename=""
) noexcept : _code{code}, _msg{""} {
static_assert(std::is_same<const std::unordered_map<unsigned int, const char*>, decltype(ErrorType::ErrorMessages)>::value,
"const std::unordered_map<unsigned int, const char*> ErrorMessages must be "
"a class member."
);
static_assert(std::is_enum_v<typename ErrorType::ErrorCodes>, "enum ErrorCodes must be a class member.");
static_assert(std::is_same<std::underlying_type_t<typename ErrorType::ErrorCodes>, unsigned int>::value,
"enum ErrorCodes must be of underlying type unsigned int."
);
static_assert(std::is_same<const std::string, decltype(ErrorType::type)>::value,
"const std::string type must be a class member."
);
_msg += std::string(ErrorType::type);
_msg += "-" + std::to_string(_code);
if (line && filename.size()) {
_msg += " in \'" + filename + "\':" + std::to_string(line);
}
_msg += "\n " + msg;
}
ErrorBase(
const unsigned int code,
std::string msg,
const std::exception& trace,
unsigned int line=0,
std::string filename=""
) noexcept : ErrorBase(code, msg, line, filename) {
std::string what_msg(trace.what());
std::string::size_type newline_pos = what_msg.find("\n");
while(newline_pos != std::string::npos) {
what_msg.replace(newline_pos, 1, "\n ");
newline_pos = what_msg.find("\n", newline_pos+1);
}
_msg += "\n Trace: " + what_msg;
}
ErrorBase(
const unsigned int code,
unsigned int line=0,
std::string filename=""
) noexcept : ErrorBase(
code,
ErrorBase<ErrorType>::lookup(code),
line,
filename
) {}
ErrorBase(
const unsigned int code,
const std::exception& trace,
unsigned int line=0,
std::string filename=""
) noexcept : ErrorBase(
code,
ErrorBase<ErrorType>::lookup(code),
trace,
line,
filename
) {}
unsigned int _code;
std::string _msg;
};
class Error : public ErrorBase<Error> {
public:
enum ErrorCodes : unsigned int {
GENERAL, UNDEFINED, BADERRORCODE
};
Error(unsigned int code) : ErrorBase<Error>(code, ErrorBase<Error>::lookup(code)) {}
Error(unsigned int code, const std::exception& trace)
: ErrorBase<Error>(code, ErrorBase<Error>::lookup(code), trace) {}
friend ErrorBase<Error>;
protected:
static const std::string type;
static const ErrorCodeMap ErrorMessages;
};
/*
template<class ErrorType>
ErrorBase<ErrorType>::ErrorBase(
unsigned int code,
std::string msg,
const std::exception& trace,
unsigned int line,
std::string filename
) noexcept
: ErrorBase<ErrorType>(code, msg, std::make_shared<Error>(trace), line, filename) {}
*/
template<class ErrorType>
std::string ErrorBase<ErrorType>::lookup(unsigned int code) {
for (auto kv : ErrorType::ErrorMessages) {
if (kv.first==code) {
return std::string(kv.second);
}
}
throw Error(Error::ErrorCodes::BADERRORCODE);
}
} // namespace nb
#endif // _NB_ERROR