35#define CPP_ERROR_UTILS_VERSION_MAJOR 1
38#define CPP_ERROR_UTILS_VERSION_MINOR 0
41#define CPP_ERROR_UTILS_VERSION_PATCH 0
54#include <system_error>
125 class ExtraErrorConditionCategory final :
public std::error_category {
127 [[nodiscard]]
const char *name()
const noexcept override {
128 return "ExtraErrorCondition";
131 [[nodiscard]] std::string message(
int ev)
const override {
134 return "Logic error";
136 return "Runtime error";
138 return "Resource error";
140 return "Access error";
142 return "Other error";
144 return "Unrecognized error condition";
151 inline const std::error_category &extra_error_condition_category() {
152 static ExtraErrorConditionCategory instance;
159 class ExtraErrorCategory final :
public std::error_category {
161 [[nodiscard]]
const char *name() const noexcept
override {
165 [[nodiscard]] std::string message(
int ev)
const override {
168 return "Invalid argument exception";
170 return "Length error exception";
172 return "Logic error exception";
176 return "Value too small (underflow exception)";
178 return "Nonexistent local time exception";
180 return "Ambiguous local time exception";
182 return "Format error exception";
184 return "Runtime error exception";
188 return "Bad allocation exception";
190 return "Bad typeid exception";
192 return "Bad cast exception";
196 return "Bad optional access exception";
198 return "Bad expected access exception";
200 return "Bad variant access exception";
202 return "Bad weak pointer exception";
204 return "Bad function call exception";
208 return "Bad exception";
210 return "Exception caught";
212 return "Unknown exception caught";
214 return "Unknown error";
216 return "Unrecognized ExtraError";
223 [[nodiscard]] std::error_condition default_error_condition(
int ev)
const noexcept override {
263 inline const std::error_category &extra_error_category() {
264 static ExtraErrorCategory instance;
273 return {
static_cast<int>(e), detail::extra_error_category()};
279 return {
static_cast<int>(e), detail::extra_error_condition_category()};
287 struct is_error_code_enum<error_utils::
ExtraError> : true_type {};
304 struct is_expected : std::false_type {};
307 template <
typename T,
typename E>
308 struct is_expected<std::expected<T, E>> : std::true_type {};
311 template <
typename T>
312 inline constexpr bool is_expected_v = is_expected<T>::value;
315 template <
typename T>
316 concept convertible_to_error_code = (std::is_error_condition_enum_v<T> || std::is_error_code_enum_v<T>) &&
317 requires { {
make_error_code(std::declval<T>()) } -> std::same_as<std::error_code>; };
320 template <
typename T>
321 concept directly_convertible_to_error_condition =
requires {
326 template <
typename T>
327 concept comparable_to_error_code = convertible_to_error_code<T> || std::is_same_v<T, std::error_code> ||
328 std::is_same_v<T, std::error_condition> || directly_convertible_to_error_condition<T>;
336 std::string context_{};
337 std::error_code error_code_{};
343 constexpr Error() noexcept = default;
349 : context_{
context}, error_code_{code} {}
354 constexpr explicit Error(
const detail::convertible_to_error_code
auto code,
const std::string_view
context = {})
360 : context_{std::move(other.context_)},
361 error_code_{other.error_code_} {}
366 context_ = other.context_;
367 error_code_ = other.error_code_;
374 context_ = std::move(other.context_);
375 error_code_ = other.error_code_;
381 constexpr friend
bool operator==(const
Error &lhs, const
Error &rhs) noexcept {
382 return lhs.error_code_ == rhs.error_code_;
386 return lhs.error_code_ <=> rhs.error_code_;
389 constexpr friend bool operator==(
const Error &lhs,
const std::error_code &rhs)
noexcept {
390 return lhs.error_code_ == rhs;
394 return lhs.error_code_ <=> rhs;
397 constexpr friend bool operator==(
const Error &lhs,
const std::error_condition &rhs)
noexcept {
398 return lhs.error_code_ == rhs;
404 <<
"\n(error_code: " << obj.error_code_.value() <<
" ("
405 << obj.error_code_.category().name() <<
" category))";
409 [[nodiscard]]
constexpr explicit operator bool() const noexcept {
410 return error_code_.operator bool();
414 [[nodiscard]]
constexpr const std::error_code &
error_code() const noexcept {
return error_code_; }
417 [[nodiscard]]
constexpr const std::string &
context() const noexcept {
return context_; }
420 [[nodiscard]]
constexpr int value() const noexcept {
return error_code_.value(); }
423 [[nodiscard]]
constexpr const std::error_category &
category() const noexcept {
424 return error_code_.category();
429 [[nodiscard]]
constexpr std::string
message()
const {
430 if (context_.empty()) {
431 return error_code_.message();
433 return std::format(
"{}: {}", context_, error_code_.message());
440 template <
typename T>
441 requires detail::comparable_to_error_code<T>
442 [[nodiscard]]
constexpr bool is(T &&code)
const noexcept {
443 if constexpr (std::is_same_v<T, Error> || std::is_same_v<T, std::error_code> ||
444 std::is_same_v<T, std::error_condition>) {
446 return code == *
this;
447 }
else if constexpr (detail::convertible_to_error_code<T>) {
448 using std::make_error_code;
450 }
else if constexpr (detail::directly_convertible_to_error_condition<T>) {
451 using std::make_error_condition;
453 }
else static_assert(
false,
"Should be unreachable.");
464 template <
typename Code,
typename... Others>
465 requires detail::comparable_to_error_code<Code> && (detail::comparable_to_error_code<Others> && ...)
466 [[nodiscard]]
constexpr bool is_any_of(Code &&code, Others &&... others)
const noexcept {
467 return is(std::forward<Code>(code)) || (
is(std::forward<Others>(others)) || ...);
474 swap(lhs.context_, rhs.context_);
475 swap(lhs.error_code_, rhs.error_code_);
482 template <
typename T =
void>
504 template <
typename T,
typename E,
typename Ctx = std::
string_view>
505 requires detail::convertible_to_error_code<E>
507 return std::unexpected(Error{std::forward<E>(code), std::forward<Ctx>(context)});
515 template <
typename T>
516 [[nodiscard]]
constexpr Result<T> make_error(
const std::error_code &code,
const std::string_view context = {}) {
517 return std::unexpected(Error{code, context});
525 template <
typename T>
527 std::string_view context = {}) {
528 auto create_unexpected = [&context]<
typename C>(C &&err_code,
const std::string_view msg) {
531 if (context.ends_with(
"\x02")) {
532 context.remove_suffix(1);
533 return std::unexpected(Error{std::forward<C>(err_code), context});
536 return std::unexpected(
Error{
537 std::forward<C>(err_code), context.empty() ? msg : std::format(
"{}: {}", context, msg)
543 case std::regex_constants::error_collate:
544 return create_unexpected(std::errc::invalid_argument,
545 "Regex error: invalid collating element name");
547 case std::regex_constants::error_ctype:
548 return create_unexpected(std::errc::invalid_argument,
549 "Regex error: invalid character class name");
551 case std::regex_constants::error_escape:
552 return create_unexpected(std::errc::invalid_argument,
553 "Regex error: invalid escaped character or a trailing escape");
555 case std::regex_constants::error_backref:
556 return create_unexpected(std::errc::invalid_argument,
557 "Regex error: invalid back reference");
559 case std::regex_constants::error_brack:
560 return create_unexpected(std::errc::invalid_argument,
561 "Regex error: mismatched square brackets ('[' and ']')");
563 case std::regex_constants::error_paren:
564 return create_unexpected(std::errc::invalid_argument,
565 "Regex error: mismatched parentheses ('(' and ')')");
567 case std::regex_constants::error_brace:
568 return create_unexpected(std::errc::invalid_argument,
569 "Regex error: mismatched curly braces ('{' and '}')");
571 case std::regex_constants::error_badbrace:
572 return create_unexpected(std::errc::invalid_argument,
573 "Regex error: invalid range in a {} expression");
575 case std::regex_constants::error_range:
576 return create_unexpected(std::errc::invalid_argument,
577 "Regex error: invalid character range");
579 case std::regex_constants::error_space:
580 return create_unexpected(std::errc::not_enough_memory,
581 "Regex error: insufficient memory to convert the expression"
582 " into a finite state machine");
584 case std::regex_constants::error_badrepeat:
585 return create_unexpected(std::errc::invalid_argument,
586 "Regex error: '*', '?', '+' or '{' was not preceded"
587 " by a valid regular expression");
589 case std::regex_constants::error_complexity:
590 return create_unexpected(std::errc::result_out_of_range,
591 "Regex error: the complexity of an attempted match"
592 " exceeded a predefined level");
594 case std::regex_constants::error_stack:
595 return create_unexpected(std::errc::not_enough_memory,
596 "Regex error: insufficient memory to perform a match");
608 return std::make_error_code(
static_cast<std::errc
>(err));
615 template <
typename T>
629 template <
typename Func,
typename R = std::invoke_result_t<Func>>
634 if constexpr (std::is_void_v<R>) {
635 std::forward<Func>(func)();
641 R result = std::forward<Func>(func)();
664 template <
typename Func>
665 requires std::is_nothrow_invocable_v<Func>
667 using R = std::invoke_result_t<Func>;
668 static_assert(std::is_integral_v<R> && std::convertible_to<R, int>,
669 "func must return an integral type convertible to int");
674 R result = std::forward<Func>(func)();
688 template <
typename Func,
typename R = std::invoke_result_t<Func>>
690 auto create_error = [&context]<
typename T>(T &&code,
const std::string_view default_msg) ->
Result<R> {
692 context.empty() ? default_msg : std::format(
"{}: {}", context, default_msg));
696 return std::forward<Func>(func)();
699 }
catch (
const std::invalid_argument &e) {
701 }
catch (
const std::domain_error &e) {
702 return create_error(std::errc::argument_out_of_domain, e.what());
703 }
catch (
const std::length_error &e) {
705 }
catch (
const std::out_of_range &e) {
706 return create_error(std::errc::result_out_of_range, e.what());
707 }
catch (
const std::future_error &e) {
708 return create_error(e.code(), e.what());
709 }
catch (
const std::logic_error &e) {
713 }
catch (
const std::range_error &e) {
714 return create_error(std::errc::result_out_of_range, e.what());
715 }
catch (
const std::overflow_error &e) {
716 return create_error(std::errc::value_too_large, e.what());
717 }
catch (
const std::underflow_error &e) {
719 }
catch (
const std::regex_error &e) {
720 return create_error(e.code(), std::format(
"{}\x02", e.what()));
721 }
catch (
const std::system_error &e) {
722 return create_error(e.code(),
"");
723 }
catch (
const std::chrono::nonexistent_local_time &e) {
725 }
catch (
const std::chrono::ambiguous_local_time &e) {
727 }
catch (
const std::format_error &e) {
729 }
catch (
const std::runtime_error &e) {
733 }
catch (
const std::bad_alloc &e) {
735 }
catch (
const std::bad_typeid &e) {
737 }
catch (
const std::bad_cast &e) {
741 }
catch (
const std::bad_optional_access &e) {
743 }
catch (
const std::bad_expected_access<void> &e) {
745 }
catch (
const std::bad_variant_access &e) {
747 }
catch (
const std::bad_weak_ptr &e) {
749 }
catch (
const std::bad_function_call &e) {
751 }
catch (
const std::bad_exception &e) {
755 }
catch (
const std::exception &e) {
766 template <
typename T>
768 if (results.size() == 0) {
769 return make_error<T>(std::errc::invalid_argument,
"No alternatives provided");
771 std::string combined_errors{};
773 for (
const auto &result : results) {
777 if (!combined_errors.empty()) {
778 combined_errors +=
"; ";
780 combined_errors += result.error().message();
789 struct formatter<error_utils::Error> {
790 static constexpr auto parse(format_parse_context &ctx) {
794 static auto format(
const error_utils::Error &error, format_context &ctx) {
795 return format_to(ctx.out(),
"{} \n(error_code: {}, category: {})",
801 struct hash<error_utils::Error> {
802 size_t operator()(
const error_utils::Error &error)
const noexcept {
803 return hash<error_code>{}(error.error_code());
A wrapper class for system error codes with additional context.
Definition error_utils.hpp:332
constexpr Error(const detail::convertible_to_error_code auto code, const std::string_view context={})
Create an error with a type convertible to std::error_code and optional context.
Definition error_utils.hpp:354
constexpr Error(const Error &other) noexcept=default
constexpr friend void swap(Error &lhs, Error &rhs) noexcept
Swap the contents of two Error objects.
Definition error_utils.hpp:472
constexpr friend bool operator==(const Error &lhs, const std::error_code &rhs) noexcept
Definition error_utils.hpp:389
constexpr friend auto operator<=>(const Error &lhs, const std::error_code &rhs) noexcept
Definition error_utils.hpp:393
constexpr bool is(T &&code) const noexcept
Check if the error is of a specific type.
Definition error_utils.hpp:442
constexpr Error & operator=(const Error &other)
Definition error_utils.hpp:363
constexpr int value() const noexcept
Returns the value of the error code.
Definition error_utils.hpp:420
constexpr Error & operator=(Error &&other) noexcept
Definition error_utils.hpp:371
constexpr std::string message() const
Get the error message including context if available.
Definition error_utils.hpp:429
constexpr Error(Error &&other) noexcept
Definition error_utils.hpp:359
constexpr const std::error_code & error_code() const noexcept
Returns a constant reference to the underlying error code.
Definition error_utils.hpp:414
constexpr const std::string & context() const noexcept
Returns a constant reference to the context string.
Definition error_utils.hpp:417
constexpr bool is_any_of(Code &&code, Others &&... others) const noexcept
Check if the error belongs to any of the specified error codes or error conditions.
Definition error_utils.hpp:466
constexpr friend std::ostream & operator<<(std::ostream &os, const Error &obj)
Definition error_utils.hpp:401
constexpr const std::error_category & category() const noexcept
Returns the category of the error code.
Definition error_utils.hpp:423
constexpr friend auto operator<=>(const Error &lhs, const Error &rhs) noexcept
Definition error_utils.hpp:385
constexpr friend bool operator==(const Error &lhs, const std::error_condition &rhs) noexcept
Definition error_utils.hpp:397
constexpr Error() noexcept=default
~Error() noexcept=default
Contains utilities for error handling and classification.
Definition error_utils.hpp:62
ExtraError
Represents specific error codes for exception handling and error classification.
Definition error_utils.hpp:73
@ format_error
std::format_error exception.
Definition error_utils.hpp:83
@ bad_alloc
std::bad_alloc exception.
Definition error_utils.hpp:87
@ bad_weak_ptr
std::bad_weak_ptr exception.
Definition error_utils.hpp:95
@ unknown_exception
catch-all for any other exceptions.
Definition error_utils.hpp:101
@ exception
all std::exception exceptions.
Definition error_utils.hpp:100
@ value_too_small
std::underflow_error exception.
Definition error_utils.hpp:80
@ bad_exception
std::bad_exception exception.
Definition error_utils.hpp:99
@ unknown_error
Unknown error (not related to exceptions).
Definition error_utils.hpp:103
@ runtime_error
std::runtime_error base exception.
Definition error_utils.hpp:84
@ nonexistent_local_time
std::chrono::nonexistent_local_time exception.
Definition error_utils.hpp:81
@ length_error
std::length_error exception.
Definition error_utils.hpp:76
@ bad_typeid
std::bad_typeid exception.
Definition error_utils.hpp:88
@ logic_error
std::logic_error base exception.
Definition error_utils.hpp:77
@ bad_optional_access
std::bad_optional_access exception.
Definition error_utils.hpp:92
@ bad_expected_access
std::bad_expected_access exception.
Definition error_utils.hpp:93
@ ambiguous_local_time
std::chrono::ambiguous_local_time exception.
Definition error_utils.hpp:82
@ invalid_argument
std::invalid_argument exception.
Definition error_utils.hpp:75
@ bad_cast
std::bad_cast exception.
Definition error_utils.hpp:89
@ bad_function_call
std::bad_function_call exception.
Definition error_utils.hpp:96
@ bad_variant_access
std::bad_variant_access exception.
Definition error_utils.hpp:94
Result<> VoidResult
Result type for void (std::expected<void, Error>)
Definition error_utils.hpp:489
ExtraErrorCondition
Represents categories of error conditions for error handling and classification.
Definition error_utils.hpp:111
@ resource_error
Errors related to resource allocation and management.
Definition error_utils.hpp:114
@ access_error
Errors related to invalid access of data structures.
Definition error_utils.hpp:115
@ runtime_error
Errors occurring during program execution.
Definition error_utils.hpp:113
@ logic_error
Errors related to program logic and invalid operations.
Definition error_utils.hpp:112
@ other_error
Other errors that do not fit into the above categories.
Definition error_utils.hpp:116
IntResult invoke_with_syscall_api(Func &&func, const std::string_view error_context={}) noexcept
Execute a function that may set errno, capturing the result and any error.
Definition error_utils.hpp:666
Result< T > make_error_from_errno(const std::string_view context={})
Create an error result from the current errno value.
Definition error_utils.hpp:616
Result< bool > BoolResult
Result type for booleans (std::expected<bool, Error>)
Definition error_utils.hpp:492
constexpr Result< T > make_error(E &&code, Ctx &&context={})
Create an error result of the specified type.
Definition error_utils.hpp:506
Result< std::string > StringResult
Result type for strings (std::expected<std::string, Error>)
Definition error_utils.hpp:490
constexpr Result< T > first_of(std::initializer_list< Result< T > > results)
Return first success result from multiple alternatives.
Definition error_utils.hpp:767
std::expected< T, Error > Result
A specialization of std::expected for the Error type.
Definition error_utils.hpp:483
constexpr std::error_condition make_error_condition(ExtraErrorCondition e)
Create an error condition from an ExtraErrorCondition enum value.
Definition error_utils.hpp:278
auto with_errno(Func &&func, const std::string_view error_context={}) -> Result< R >
Execute a function that may set errno, capturing the result and any error.
Definition error_utils.hpp:630
Result< int > IntResult
Result type for integers (std::expected<int, Error>)
Definition error_utils.hpp:491
constexpr auto try_catch(Func &&func, std::string_view context={}) -> Result< R >
Execute a function and catch common exceptions, converting them to errors.
Definition error_utils.hpp:689
std::error_code last_error() noexcept
Retrieve the last system error code and reset errno.
Definition error_utils.hpp:605
constexpr std::error_code make_error_code(ExtraError e)
Create an error code from an ExtraError enum value.
Definition error_utils.hpp:272