From 8506a2aa00768382e8575f951fccacbc5cad85e5 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Sat, 5 Apr 2025 21:57:20 +0200 Subject: [PATCH] libutil: add ForeignException this wraps a non-lix exception so we'll be able to add async traces. to not interfere with existing infrastructure we also add a BaseException, deriving from BaseError would have foreign exceptions match on the very broad catches we have in libmain even though we want these logged only. Change-Id: I5545788c299e9bbdb4d730458914bfd816870794 --- lix/libutil/error.hh | 63 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/lix/libutil/error.hh b/lix/libutil/error.hh index bd838f635..34c5fb189 100644 --- a/lix/libutil/error.hh +++ b/lix/libutil/error.hh @@ -20,6 +20,7 @@ #include "lix/libutil/fmt.hh" #include +#include #include #include #include @@ -93,11 +94,19 @@ struct ErrorInfo { std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace); +/** + * Base class for both errors we can handle (c.f. `BaseError`) and anything + * we want to log and terminate when encountered (c.f. `ForeignException`). + */ +struct BaseException : public std::exception +{ +}; + /** * BaseError should generally not be caught, as it has Interrupted as * a subclass. Catch Error instead. */ -class BaseError : public std::exception +class BaseError : public BaseException { protected: mutable ErrorInfo err; @@ -186,6 +195,58 @@ MakeError(Error, BaseError); MakeError(UsageError, Error); MakeError(UnimplementedError, Error); +/** + * Wrap any exception as BaseException. We don't wrap as Error because we do not + * usually want to catch these exceptions and we don't use std::nested_exception + * because we need the dynamic type to preserve the original exception for that. + * This should never be used to wrap something that already is a BaseError (or a + * BaseException), but this isn't checked since this should not be thrown often. + */ +class ForeignException : public BaseException +{ + std::shared_ptr _what; + +public: + const std::exception_ptr inner; + const std::type_info & innerType; + + explicit ForeignException(const std::exception & inner) + : _what(std::make_shared(inner.what())) + , inner(std::make_exception_ptr(inner)) + , innerType(typeid(inner)) + { + } + + [[noreturn]] + void rethrow() const + { + std::rethrow_exception(inner); + } + + template + E * as() const + { + try { + rethrow(); + } catch (E & e) { // NOLINT(lix-foreign-exceptions) + return &e; + } catch (...) { + return nullptr; + } + } + + template + bool is() const + { + return as() != nullptr; + } + + const char * what() const noexcept override + { + return _what->c_str(); + } +}; + class SysError : public Error { public: