From 93087ec1ac8fee2ceb15d9a087badc27e5a6a8cb Mon Sep 17 00:00:00 2001 From: Redbeanw44602 Date: Mon, 3 Mar 2025 13:03:51 +0800 Subject: [PATCH] feat: new exception handling model. --- src/data_format/bound_symbol_list.cpp | 10 +-- src/data_format/raw_text.cpp | 6 +- src/data_format/type/decl_type.h | 4 +- src/data_format/typed_symbol_list.cpp | 11 +-- src/data_format/typed_symbol_list.h | 16 ++++ src/error.h | 124 ++++++++++++++++++++++++++ src/io/io_base.h | 16 ++++ src/io/streamed_io.cpp | 4 +- src/tools/askrva/main.cpp | 6 +- src/tools/blob-extractor/main.cpp | 9 +- src/tools/extractsym/main.cpp | 4 +- src/tools/makepdb/main.cpp | 4 +- 12 files changed, 181 insertions(+), 33 deletions(-) create mode 100644 src/error.h diff --git a/src/data_format/bound_symbol_list.cpp b/src/data_format/bound_symbol_list.cpp index ec418af..5407c50 100644 --- a/src/data_format/bound_symbol_list.cpp +++ b/src/data_format/bound_symbol_list.cpp @@ -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) { diff --git a/src/data_format/raw_text.cpp b/src/data_format/raw_text.cpp index a5fbf36..aada549 100644 --- a/src/data_format/raw_text.cpp +++ b/src/data_format/raw_text.cpp @@ -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; } diff --git a/src/data_format/type/decl_type.h b/src/data_format/type/decl_type.h index d6ee47b..941eb30 100644 --- a/src/data_format/type/decl_type.h +++ b/src/data_format/type/decl_type.h @@ -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 diff --git a/src/data_format/typed_symbol_list.cpp b/src/data_format/typed_symbol_list.cpp index 296559a..55d2715 100644 --- a/src/data_format/typed_symbol_list.cpp +++ b/src/data_format/typed_symbol_list.cpp @@ -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); diff --git a/src/data_format/typed_symbol_list.h b/src/data_format/typed_symbol_list.h index 635f1b0..295c896 100644 --- a/src/data_format/typed_symbol_list.h +++ b/src/data_format/typed_symbol_list.h @@ -26,4 +26,20 @@ private: std::unordered_set m_data; }; +class MissingDeclTypeException +: public RuntimeException { +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 diff --git a/src/error.h b/src/error.h new file mode 100644 index 0000000..3b21b7e --- /dev/null +++ b/src/error.h @@ -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 +class RuntimeException : public BaseException { +public: + template + explicit RuntimeException( + const std::format_string fmt, + Args&&... args + ) + : m_reason(std::format(fmt, std::forward(args)...)), + m_stacktrace(std::stacktrace::current()) {} + + constexpr std::string category() const { + return static_cast(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 = ""; + 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 + constexpr void add_context(const std::string& key, T&& value) { + m_context_information[key] = std::format("{}", std::forward(value)); + } + +private: + std::string m_reason; + + std::unordered_map m_context_information; + std::stacktrace m_stacktrace; +}; + +class UnixException : public RuntimeException { +public: + explicit UnixException() + : RuntimeException("{}", std::strerror(errno)) { + add_context("errno", errno); + } + + constexpr std::string category() const { return "exception.unix"; } +}; + +class EnumTransformException : public RuntimeException { +public: + // TODO: compile-time reflection. + // TODO: remove helper. + + template + 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 + 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 diff --git a/src/io/io_base.h b/src/io/io_base.h index 348baf3..39cf3b1 100644 --- a/src/io/io_base.h +++ b/src/io/io_base.h @@ -10,4 +10,20 @@ public: virtual void write(const fs::path& path) const = 0; }; +class IOException : public RuntimeException { +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 diff --git a/src/io/streamed_io.cpp b/src/io/streamed_io.cpp index f9696e5..98b3a7f 100644 --- a/src/io/streamed_io.cpp +++ b/src/io/streamed_io.cpp @@ -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 { diff --git a/src/tools/askrva/main.cpp b/src/tools/askrva/main.cpp index d453197..a884e50 100644 --- a/src/tools/askrva/main.cpp +++ b/src/tools/askrva/main.cpp @@ -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; } diff --git a/src/tools/blob-extractor/main.cpp b/src/tools/blob-extractor/main.cpp index 7ad9bdb..f47d3e1 100644 --- a/src/tools/blob-extractor/main.cpp +++ b/src/tools/blob-extractor/main.cpp @@ -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; } diff --git a/src/tools/extractsym/main.cpp b/src/tools/extractsym/main.cpp index 29a9406..d4f0a07 100644 --- a/src/tools/extractsym/main.cpp +++ b/src/tools/extractsym/main.cpp @@ -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; } diff --git a/src/tools/makepdb/main.cpp b/src/tools/makepdb/main.cpp index 9492039..7fb03f9 100644 --- a/src/tools/makepdb/main.cpp +++ b/src/tools/makepdb/main.cpp @@ -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; }