feat: new exception handling model.
This commit is contained in:
@@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
namespace di::data_format {
|
namespace di::data_format {
|
||||||
|
|
||||||
|
using namespace io;
|
||||||
|
|
||||||
void BoundSymbolList::read(const fs::path& path) {
|
void BoundSymbolList::read(const fs::path& path) {
|
||||||
std::ifstream ifs(path);
|
std::ifstream ifs(path);
|
||||||
if (!ifs) {
|
if (!ifs) throw UnableToOpenException(path);
|
||||||
throw std::runtime_error("Failed to open data path.");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto data = nlohmann::json::parse(ifs);
|
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 {
|
void BoundSymbolList::write(const fs::path& path) const {
|
||||||
std::ofstream ofs(path);
|
std::ofstream ofs(path);
|
||||||
if (!ofs) {
|
if (!ofs) throw UnableToOpenException(path);
|
||||||
throw std::runtime_error("Failed to open file!");
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json data;
|
nlohmann::json data;
|
||||||
for (const auto& entity : m_entities) {
|
for (const auto& entity : m_entities) {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "data_format/raw_text.h"
|
#include "data_format/raw_text.h"
|
||||||
|
|
||||||
|
using namespace di::io;
|
||||||
|
|
||||||
namespace di::data_format {
|
namespace di::data_format {
|
||||||
|
|
||||||
void RawText::read(const fs::path& path) {
|
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 {
|
void RawText::write(const fs::path& path) const {
|
||||||
std::ofstream ofs(path);
|
std::ofstream ofs(path);
|
||||||
if (!ofs) {
|
if (!ofs) throw UnableToOpenException(path);
|
||||||
throw std::runtime_error("Failed to open save file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
ofs << m_data;
|
ofs << m_data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
HSTR(VarTemplateSpecialization);
|
HSTR(VarTemplateSpecialization);
|
||||||
#undef HSTR
|
#undef HSTR
|
||||||
default:
|
default:
|
||||||
throw std::invalid_argument("Unexpected decl type.");
|
throw EnumTransformException(str, Enum{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
@@ -87,7 +87,7 @@ public:
|
|||||||
HSTR(VarTemplateSpecialization);
|
HSTR(VarTemplateSpecialization);
|
||||||
#undef HSTR
|
#undef HSTR
|
||||||
default:
|
default:
|
||||||
throw std::runtime_error("Unexpected decl type.");
|
throw EnumTransformException(m_data, Enum{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "data_format/typed_symbol_list.h"
|
#include "data_format/typed_symbol_list.h"
|
||||||
|
|
||||||
|
using namespace di::io;
|
||||||
|
|
||||||
namespace di::data_format {
|
namespace di::data_format {
|
||||||
|
|
||||||
void TypedSymbolList::read(const fs::path& path) {
|
void TypedSymbolList::read(const fs::path& path) {
|
||||||
std::ifstream ifs(path);
|
std::ifstream ifs(path);
|
||||||
if (!ifs) {
|
if (!ifs) throw UnableToOpenException(path);
|
||||||
throw std::runtime_error("Failed to open symlist file.");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(ifs, line)) {
|
while (std::getline(ifs, line)) {
|
||||||
@@ -14,10 +14,7 @@ void TypedSymbolList::read(const fs::path& path) {
|
|||||||
|
|
||||||
auto sep_pos = line.find(", ");
|
auto sep_pos = line.find(", ");
|
||||||
if (sep_pos == std::string::npos) {
|
if (sep_pos == std::string::npos) {
|
||||||
throw std::runtime_error(
|
throw MissingDeclTypeException(path, line);
|
||||||
"Symbol data is not included declType, please re-generate "
|
|
||||||
"symlist file with -record-decl-name."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto decl_type_s = line.substr(0, sep_pos);
|
auto decl_type_s = line.substr(0, sep_pos);
|
||||||
|
|||||||
@@ -26,4 +26,20 @@ private:
|
|||||||
std::unordered_set<TypedSymbol> m_data;
|
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
|
} // namespace di::data_format
|
||||||
|
|||||||
124
src/error.h
Normal file
124
src/error.h
Normal 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
|
||||||
@@ -10,4 +10,20 @@ public:
|
|||||||
virtual void write(const fs::path& path) const = 0;
|
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
|
} // namespace di::io
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ namespace di::io {
|
|||||||
|
|
||||||
void StreamedIO::read(const fs::path& path) {
|
void StreamedIO::read(const fs::path& path) {
|
||||||
m_file_stream.open(path, std::ios::binary);
|
m_file_stream.open(path, std::ios::binary);
|
||||||
if (!m_file_stream) {
|
if (!m_file_stream) throw UnableToOpenException(path);
|
||||||
throw std::runtime_error("Failed to open file!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamedIO::write(const fs::path& path) const {
|
void StreamedIO::write(const fs::path& path) const {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ int main(int argc, char* argv[]) try {
|
|||||||
|
|
||||||
std::println("Everything is OK.");
|
std::println("Everything is OK.");
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& e) {
|
} catch (const BaseException& e) {
|
||||||
std::println("E: {}", e.what());
|
std::cerr << e;
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
using namespace di;
|
using namespace di;
|
||||||
using namespace di::data_format;
|
using namespace di::data_format;
|
||||||
|
using namespace di::io;
|
||||||
|
|
||||||
auto load_args(int argc, char* argv[]) {
|
auto load_args(int argc, char* argv[]) {
|
||||||
argparse::ArgumentParser program("blob-extractor");
|
argparse::ArgumentParser program("blob-extractor");
|
||||||
@@ -47,14 +48,12 @@ int main(int argc, char* argv[]) try {
|
|||||||
});
|
});
|
||||||
|
|
||||||
std::ofstream ofs(args.m_output_path);
|
std::ofstream ofs(args.m_output_path);
|
||||||
if (!ofs) {
|
if (!ofs) throw UnableToOpenException(args.m_output_path);
|
||||||
throw std::runtime_error("Failed to open file!");
|
|
||||||
}
|
|
||||||
|
|
||||||
ofs << data.dump(4);
|
ofs << data.dump(4);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& e) {
|
} catch (const BaseException& e) {
|
||||||
std::println("E: {}", e.what());
|
std::cerr << e;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ int main(int argc, char* argv[]) try {
|
|||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& e) {
|
} catch (const BaseException& e) {
|
||||||
std::println("E: {}", e.what());
|
std::cerr << e;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ int main(int argc, char* argv[]) try {
|
|||||||
pdb.write(args.output_path);
|
pdb.write(args.output_path);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& e) {
|
} catch (const BaseException& e) {
|
||||||
std::println("E: {}", e.what());
|
std::cerr << e;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user