feat: new exception handling model.

This commit is contained in:
2025-03-03 13:03:51 +08:00
parent d679941f97
commit 93087ec1ac
12 changed files with 181 additions and 33 deletions

View File

@@ -4,11 +4,11 @@
namespace di::data_format {
using namespace io;
void BoundSymbolList::read(const fs::path& path) {
std::ifstream ifs(path);
if (!ifs) {
throw std::runtime_error("Failed to open data path.");
}
if (!ifs) throw UnableToOpenException(path);
auto data = nlohmann::json::parse(ifs);
@@ -20,9 +20,7 @@ void BoundSymbolList::read(const fs::path& path) {
void BoundSymbolList::write(const fs::path& path) const {
std::ofstream ofs(path);
if (!ofs) {
throw std::runtime_error("Failed to open file!");
}
if (!ofs) throw UnableToOpenException(path);
nlohmann::json data;
for (const auto& entity : m_entities) {

View File

@@ -1,5 +1,7 @@
#include "data_format/raw_text.h"
using namespace di::io;
namespace di::data_format {
void RawText::read(const fs::path& path) {
@@ -12,9 +14,7 @@ void RawText::read(const fs::path& path) {
void RawText::write(const fs::path& path) const {
std::ofstream ofs(path);
if (!ofs) {
throw std::runtime_error("Failed to open save file.");
}
if (!ofs) throw UnableToOpenException(path);
ofs << m_data;
}

View File

@@ -49,7 +49,7 @@ public:
HSTR(VarTemplateSpecialization);
#undef HSTR
default:
throw std::invalid_argument("Unexpected decl type.");
throw EnumTransformException(str, Enum{});
}
// clang-format on
@@ -87,7 +87,7 @@ public:
HSTR(VarTemplateSpecialization);
#undef HSTR
default:
throw std::runtime_error("Unexpected decl type.");
throw EnumTransformException(m_data, Enum{});
}
// clang-format on

View File

@@ -1,12 +1,12 @@
#include "data_format/typed_symbol_list.h"
using namespace di::io;
namespace di::data_format {
void TypedSymbolList::read(const fs::path& path) {
std::ifstream ifs(path);
if (!ifs) {
throw std::runtime_error("Failed to open symlist file.");
}
if (!ifs) throw UnableToOpenException(path);
std::string line;
while (std::getline(ifs, line)) {
@@ -14,10 +14,7 @@ void TypedSymbolList::read(const fs::path& path) {
auto sep_pos = line.find(", ");
if (sep_pos == std::string::npos) {
throw std::runtime_error(
"Symbol data is not included declType, please re-generate "
"symlist file with -record-decl-name."
);
throw MissingDeclTypeException(path, line);
}
auto decl_type_s = line.substr(0, sep_pos);

View File

@@ -26,4 +26,20 @@ private:
std::unordered_set<TypedSymbol> m_data;
};
class MissingDeclTypeException
: public RuntimeException<MissingDeclTypeException> {
public:
explicit MissingDeclTypeException(
const fs::path& list_file_path,
std::string_view current_line
)
: RuntimeException(
"The symbol data file does not contain the decl_type. Please "
"regenerate it using the -record-decl-name option."
) {
add_context("path", list_file_path.string());
add_context("current_line", current_line);
}
};
} // namespace di::data_format

124
src/error.h Normal file
View File

@@ -0,0 +1,124 @@
#pragma once
namespace di {
class BaseException {
public:
virtual std::string what() const = 0;
friend std::ostream& operator<<(std::ostream& os, const BaseException& e) {
os << e.what();
return os;
}
protected:
BaseException() = default;
};
template <typename Derived>
class RuntimeException : public BaseException {
public:
template <typename... Args>
explicit RuntimeException(
const std::format_string<Args...> fmt,
Args&&... args
)
: m_reason(std::format(fmt, std::forward<Args>(args)...)),
m_stacktrace(std::stacktrace::current()) {}
constexpr std::string category() const {
return static_cast<const Derived*>(this)->category();
}
std::string what() const override {
// [exception.base] Reason...
//
// Context Information:
// val = 1
//
// StackTrace:
// #1 example_func(int) at /app/example.cpp:12
// #2 ...
// ...
std::string context_information;
if (!m_context_information.empty()) {
context_information = "\n\nContext Information: \n";
for (const auto& [key, value] : m_context_information) {
context_information += std::format(" {} = {}", key, value);
}
}
std::string stacktrace = "\n\nStackTrace: \n";
int stack_idx = -1; // ignore first entry.
for (const auto& entry : m_stacktrace) {
stack_idx++;
if (stack_idx == 0) continue;
auto func_name = entry.description();
auto source_file = entry.source_file();
if (func_name.empty()) func_name = "<unknown>";
if (source_file.empty()) source_file = "<\?\?>";
stacktrace += std::format(
" #{} {} at {}:{}",
stack_idx,
func_name,
source_file,
entry.source_line()
);
}
return std::format(
"[{}] {}{}{}\n",
category(),
m_reason,
context_information,
stacktrace
);
}
protected:
template <typename T>
constexpr void add_context(const std::string& key, T&& value) {
m_context_information[key] = std::format("{}", std::forward<T>(value));
}
private:
std::string m_reason;
std::unordered_map<std::string, std::string> m_context_information;
std::stacktrace m_stacktrace;
};
class UnixException : public RuntimeException<UnixException> {
public:
explicit UnixException()
: RuntimeException<UnixException>("{}", std::strerror(errno)) {
add_context("errno", errno);
}
constexpr std::string category() const { return "exception.unix"; }
};
class EnumTransformException : public RuntimeException<EnumTransformException> {
public:
// TODO: compile-time reflection.
// TODO: remove helper.
template <typename T>
explicit EnumTransformException(int enum_val, T _helper = {})
: RuntimeException("Unable to convert string to enumeration value because "
"input value is bad.") {
add_context("enum_type", typeid(T).name());
add_context("value", enum_val);
}
template <typename T>
explicit EnumTransformException(std::string_view enum_str, T _helper = {})
: RuntimeException("Unable to convert enumeration value to string because "
"input value is bad.") {
add_context("enum_type", typeid(T).name());
add_context("string", enum_str);
}
};
} // namespace di

View File

@@ -10,4 +10,20 @@ public:
virtual void write(const fs::path& path) const = 0;
};
class IOException : public RuntimeException<IOException> {
public:
using RuntimeException::RuntimeException;
constexpr std::string category() const { return "exception.io"; }
};
class UnableToOpenException : public UnixException {
public:
explicit UnableToOpenException(const fs::path& path) {
add_context("path", path.string());
}
constexpr std::string category() const { return "exception.io.cantopen"; }
};
} // namespace di::io

View File

@@ -4,9 +4,7 @@ namespace di::io {
void StreamedIO::read(const fs::path& path) {
m_file_stream.open(path, std::ios::binary);
if (!m_file_stream) {
throw std::runtime_error("Failed to open file!");
}
if (!m_file_stream) throw UnableToOpenException(path);
}
void StreamedIO::write(const fs::path& path) const {

View File

@@ -105,7 +105,7 @@ int main(int argc, char* argv[]) try {
std::println("Everything is OK.");
return 0;
} catch (const std::exception& e) {
std::println("E: {}", e.what());
return -1;
} catch (const BaseException& e) {
std::cerr << e;
return 1;
}

View File

@@ -5,6 +5,7 @@
using namespace di;
using namespace di::data_format;
using namespace di::io;
auto load_args(int argc, char* argv[]) {
argparse::ArgumentParser program("blob-extractor");
@@ -47,14 +48,12 @@ int main(int argc, char* argv[]) try {
});
std::ofstream ofs(args.m_output_path);
if (!ofs) {
throw std::runtime_error("Failed to open file!");
}
if (!ofs) throw UnableToOpenException(args.m_output_path);
ofs << data.dump(4);
return 0;
} catch (const std::exception& e) {
std::println("E: {}", e.what());
} catch (const BaseException& e) {
std::cerr << e;
return -1;
}

View File

@@ -59,7 +59,7 @@ int main(int argc, char* argv[]) try {
return 0;
} catch (const std::exception& e) {
std::println("E: {}", e.what());
} catch (const BaseException& e) {
std::cerr << e;
return -1;
}

View File

@@ -69,7 +69,7 @@ int main(int argc, char* argv[]) try {
pdb.write(args.output_path);
return 0;
} catch (const std::exception& e) {
std::println("E: {}", e.what());
} catch (const BaseException& e) {
std::cerr << e;
return -1;
}