Update cpptrace exceptions to defer trace generation to the callsite with a default argument

This commit is contained in:
Jeremy 2024-02-04 13:40:12 -06:00
parent 2f7f5107a5
commit ab2d440a00
No known key found for this signature in database
GPG Key ID: BE03111EB7ED6E2E
3 changed files with 82 additions and 59 deletions

View File

@ -320,13 +320,13 @@ interface or type system but this seems to be the best way to do this.
```cpp
namespace cpptrace {
class lazy_exception : public exception {
mutable detail::lazy_trace_holder trace_holder; // basically std::variant<raw_trace, stacktrace>, more docs later
// lazy_trace_holder is basically a std::variant<raw_trace, stacktrace>, more docs later
mutable detail::lazy_trace_holder trace_holder;
mutable std::string what_string;
protected:
explicit lazy_exception(std::size_t skip, std::size_t max_depth) noexcept;
explicit lazy_exception(std::size_t skip) noexcept;
public:
explicit lazy_exception() noexcept : lazy_exception(1) {}
explicit lazy_exception(
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept : trace_holder(std::move(trace)) {}
const char* what() const noexcept override;
const char* message() const noexcept override;
const stacktrace& trace() const noexcept override;
@ -341,19 +341,22 @@ well as a number of traced exception classes resembling `<stdexcept>`:
```cpp
namespace cpptrace {
class CPPTRACE_EXPORT exception_with_message : public lazy_exception {
class exception_with_message : public lazy_exception {
mutable std::string user_message;
protected:
explicit exception_with_message(std::string&& message_arg, std::size_t skip) noexcept;
explicit exception_with_message(std::string&& message_arg, std::size_t skip, std::size_t max_depth) noexcept;
public:
explicit exception_with_message(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit exception_with_message(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept : lazy_exception(std::move(trace)), user_message(std::move(message_arg)) {}
const char* message() const noexcept override;
};
// All stdexcept errors have analogs here. Same constructor as exception_with_message.
// All stdexcept errors have analogs here. All have the constructor:
// explicit the_error(
// std::string&& message_arg,
// raw_trace&& trace = detail::get_raw_trace_and_absorb()
// ) noexcept
// : exception_with_message(std::move(message_arg), std::move(trace)) {}
class logic_error : public exception_with_message { ... };
class domain_error : public exception_with_message { ... };
class invalid_argument : public exception_with_message { ... };

View File

@ -289,6 +289,9 @@ namespace cpptrace {
private:
void clear();
};
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth) noexcept;
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip = 0) noexcept;
}
// Interface for a traced exception object
@ -301,17 +304,14 @@ namespace cpptrace {
// Cpptrace traced exception object
// I hate to have to expose anything about implementation detail but the idea here is that
// TODO: CPPTRACE_FORCE_NO_INLINE annotations
class CPPTRACE_EXPORT lazy_exception : public exception {
mutable detail::lazy_trace_holder trace_holder;
mutable std::string what_string;
protected:
explicit lazy_exception(std::size_t skip, std::size_t max_depth) noexcept;
explicit lazy_exception(std::size_t skip) noexcept : lazy_exception(skip + 1, SIZE_MAX) {}
public:
explicit lazy_exception() noexcept : lazy_exception(1) {}
explicit lazy_exception(
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept : trace_holder(std::move(trace)) {}
// std::exception
const char* what() const noexcept override;
// cpptrace::exception
@ -322,87 +322,105 @@ namespace cpptrace {
class CPPTRACE_EXPORT exception_with_message : public lazy_exception {
mutable std::string user_message;
protected:
explicit exception_with_message(
std::string&& message_arg,
std::size_t skip
) noexcept : lazy_exception(skip + 1), user_message(std::move(message_arg)) {}
explicit exception_with_message(
std::string&& message_arg,
std::size_t skip,
std::size_t max_depth
) noexcept : lazy_exception(skip + 1, max_depth), user_message(std::move(message_arg)) {}
public:
explicit exception_with_message(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit exception_with_message(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept : lazy_exception(std::move(trace)), user_message(std::move(message_arg)) {}
const char* message() const noexcept override;
};
class CPPTRACE_EXPORT logic_error : public exception_with_message {
public:
explicit logic_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit logic_error(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT domain_error : public exception_with_message {
public:
explicit domain_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit domain_error(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT invalid_argument : public exception_with_message {
public:
explicit invalid_argument(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit invalid_argument(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT length_error : public exception_with_message {
public:
explicit length_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit length_error(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT out_of_range : public exception_with_message {
public:
explicit out_of_range(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit out_of_range(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT runtime_error : public exception_with_message {
public:
explicit runtime_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit runtime_error(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT range_error : public exception_with_message {
public:
explicit range_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit range_error(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT overflow_error : public exception_with_message {
public:
explicit overflow_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit overflow_error(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT underflow_error : public exception_with_message {
public:
explicit underflow_error(std::string&& message_arg) noexcept
: exception_with_message(std::move(message_arg), 1) {}
explicit underflow_error(
std::string&& message_arg,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: exception_with_message(std::move(message_arg), std::move(trace)) {}
};
class CPPTRACE_EXPORT nested_exception : public lazy_exception {
std::exception_ptr ptr;
mutable std::string message_value;
public:
explicit nested_exception(std::exception_ptr exception_ptr) noexcept
: lazy_exception(1), ptr(exception_ptr) {}
explicit nested_exception(std::exception_ptr exception_ptr, std::size_t skip) noexcept
: lazy_exception(skip + 1), ptr(exception_ptr) {}
explicit nested_exception(
std::exception_ptr exception_ptr,
raw_trace&& trace = detail::get_raw_trace_and_absorb()
) noexcept
: lazy_exception(std::move(trace)), ptr(exception_ptr) {}
const char* message() const noexcept override;
std::exception_ptr nested_ptr() const noexcept;

View File

@ -459,6 +459,11 @@ namespace cpptrace {
}
}
CPPTRACE_FORCE_NO_INLINE
raw_trace get_raw_trace_and_absorb(std::size_t skip) noexcept {
return get_raw_trace_and_absorb(skip + 1, SIZE_MAX);
}
lazy_trace_holder::lazy_trace_holder(const lazy_trace_holder& other) : resolved(other.resolved) {
if(other.resolved) {
new (&resolved_trace) stacktrace(other.resolved_trace);
@ -539,9 +544,6 @@ namespace cpptrace {
}
}
lazy_exception::lazy_exception(std::size_t skip, std::size_t max_depth) noexcept
: trace_holder(detail::get_raw_trace_and_absorb(skip + 1, max_depth)) {}
const char* lazy_exception::what() const noexcept {
if(what_string.empty()) {
what_string = message() + std::string(":\n") + trace_holder.get_resolved_trace().to_string();
@ -585,7 +587,7 @@ namespace cpptrace {
} catch(cpptrace::exception&) {
throw; // already a cpptrace::exception
} catch(...) {
throw nested_exception(std::current_exception(), skip + 1);
throw nested_exception(std::current_exception(), detail::get_raw_trace_and_absorb(skip + 1));
}
}
}