-
Notifications
You must be signed in to change notification settings - Fork 8
feat(error_handling): Port error_handling library and related unit tests from CLP core.
#28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Bill-hbrhbr
merged 7 commits into
y-scope:main
from
Bill-hbrhbr:add-error-handling-errorcode
Feb 26, 2025
Merged
Changes from 1 commit
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
88a4c3e
Port ErrorCode system
Bill-hbrhbr 6e7ac3d
Redo test file naming and namespace conventions
Bill-hbrhbr 6d86059
move everything test-related into test subfolder
Bill-hbrhbr e1e06d8
Update file names to newest convention
Bill-hbrhbr 62ccb44
Update src/ystdlib/error_handling/test/test_ErrorCode.cpp
Bill-hbrhbr 1eee1ee
Reorder functions
Bill-hbrhbr 783ceed
Remove unused header
Bill-hbrhbr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| add_subdirectory(testlib) | ||
| add_subdirectory(error_handling) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| cpp_library( | ||
| NAME error_handling | ||
| NAMESPACE ystdlib | ||
| TESTS_SOURCES | ||
| test-Defs.hpp | ||
| test-Defs.cpp | ||
| test-ErrorCode.cpp | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| #ifndef YSTDLIB_ERROR_HANDLING_ERRORCODE_HPP | ||
| #define YSTDLIB_ERROR_HANDLING_ERRORCODE_HPP | ||
|
|
||
| #include <concepts> | ||
| #include <string> | ||
| #include <system_error> | ||
| #include <type_traits> | ||
|
|
||
| namespace ystdlib::error_handling { | ||
| /** | ||
| * Concept that defines a template parameter of an integer-based error code enumeration. | ||
| * @tparam Type | ||
| */ | ||
| template <typename Type> | ||
| concept ErrorCodeEnumType = std::is_enum_v<Type> && requires(Type type) { | ||
| { | ||
| static_cast<std::underlying_type_t<Type>>(type) | ||
| } -> std::convertible_to<int>; | ||
| }; | ||
|
|
||
| /** | ||
| * Template that defines a `std::error_category` of the given set of error code enumeration. | ||
| * @tparam ErrorCodeEnum | ||
| */ | ||
| template <ErrorCodeEnumType ErrorCodeEnum> | ||
| class ErrorCategory : public std::error_category { | ||
| public: | ||
| // Methods implementing `std::error_category` | ||
| /** | ||
| * Gets the error category name. | ||
| * Note: A specialization must be explicitly implemented for each valid `ErrorCodeEnum`. | ||
| * @return The name of the error category. | ||
| */ | ||
| [[nodiscard]] auto name() const noexcept -> char const* override; | ||
|
|
||
| /** | ||
| * Gets the descriptive message associated with the given error. | ||
| * @param error_num | ||
| * @return The descriptive message for the error. | ||
| */ | ||
| [[nodiscard]] auto message(int error_num) const -> std::string override { | ||
| return message(static_cast<ErrorCodeEnum>(error_num)); | ||
| } | ||
|
|
||
| /** | ||
| * @param error_num | ||
| * @param condition | ||
| * @return Whether the error condition of the given error matches the given condition. | ||
| */ | ||
| [[nodiscard]] auto | ||
| equivalent(int error_num, std::error_condition const& condition) const noexcept | ||
| -> bool override { | ||
| return equivalent(static_cast<ErrorCodeEnum>(error_num), condition); | ||
| } | ||
|
|
||
| // Methods | ||
| /** | ||
| * Gets the descriptive message associated with the given error. | ||
| * Note: A specialization must be explicitly implemented for each valid `ErrorCodeEnum`. | ||
| * @param error_enum. | ||
| * @return The descriptive message for the error. | ||
| */ | ||
| [[nodiscard]] auto message(ErrorCodeEnum error_enum) const -> std::string; | ||
|
|
||
| /** | ||
| * Note: A specialization can be implemented to create error enum to error condition mappings. | ||
| * @param error_num | ||
| * @param condition | ||
| * @return Whether the error condition of the given error matches the given condition. | ||
| */ | ||
| [[nodiscard]] auto | ||
| equivalent(ErrorCodeEnum error_enum, std::error_condition const& condition) const noexcept | ||
| -> bool; | ||
| }; | ||
|
|
||
| /** | ||
| * Template class that defines an error code. An error code is represented by a error enum value and | ||
| * the associated error category. This template class is designed to be `std::error_code` | ||
| * compatible, meaning that every instance of this class can be used to construct a corresponded | ||
| * `std::error_code` instance, or compare with a `std::error_code` instance to inspect a specific | ||
| * error. | ||
| * @tparam ErrorCodeEnum | ||
| */ | ||
| template <ErrorCodeEnumType ErrorCodeEnum> | ||
| class ErrorCode { | ||
| public: | ||
| // Constructor | ||
| ErrorCode(ErrorCodeEnum error) : m_error{error} {} | ||
|
|
||
| /** | ||
| * @return The underlying error code enum. | ||
| */ | ||
| [[nodiscard]] auto get_error() const -> ErrorCodeEnum { return m_error; } | ||
|
|
||
| /** | ||
| * @return The error code as an error number. | ||
| */ | ||
| [[nodiscard]] auto get_error_num() const -> int { return static_cast<int>(m_error); } | ||
|
|
||
| /** | ||
| * @return The reference to the singleton of the corresponded error category. | ||
| */ | ||
| [[nodiscard]] constexpr static auto get_category() -> ErrorCategory<ErrorCodeEnum> const& { | ||
| return cCategory; | ||
| } | ||
|
|
||
| private: | ||
| static inline ErrorCategory<ErrorCodeEnum> const cCategory; | ||
|
|
||
| ErrorCodeEnum m_error; | ||
| }; | ||
|
|
||
| /** | ||
| * @tparam ErrorCodeEnum | ||
| * @param error | ||
| * @return Constructed `std::error_code` from the given `ErrorCode` instance. | ||
| */ | ||
| template <typename ErrorCodeEnum> | ||
| [[nodiscard]] auto make_error_code(ErrorCode<ErrorCodeEnum> error) -> std::error_code; | ||
|
|
||
| template <ErrorCodeEnumType ErrorCodeEnum> | ||
| auto ErrorCategory<ErrorCodeEnum>::equivalent( | ||
| ErrorCodeEnum error_enum, | ||
| std::error_condition const& condition | ||
| ) const noexcept -> bool { | ||
| return std::error_category::default_error_condition(static_cast<int>(error_enum)) == condition; | ||
| } | ||
|
|
||
| template <typename ErrorCodeEnum> | ||
| auto make_error_code(ErrorCode<ErrorCodeEnum> error) -> std::error_code { | ||
| return {error.get_error_num(), ErrorCode<ErrorCodeEnum>::get_category()}; | ||
| } | ||
| } // namespace ystdlib::error_handling | ||
|
|
||
| /** | ||
| * The macro to create a specialization of `std::is_error_code_enum` for a given type T. Only types | ||
| * that are marked with this macro will be considered as a valid YStdlib error code enum, and thus | ||
| * used to specialize `ErrorCode` and `ErrorCategory` templates. | ||
| */ | ||
| // NOLINTBEGIN(bugprone-macro-parentheses, cppcoreguidelines-macro-usage) | ||
| #define YSTDLIB_ERROR_HANDLING_MARK_AS_ERROR_CODE_ENUM(T) \ | ||
| template <> \ | ||
| struct std::is_error_code_enum<ystdlib::error_handling::ErrorCode<T>> : std::true_type { \ | ||
| static_assert(std::is_enum_v<T>); \ | ||
| }; | ||
| // NOLINTEND(bugprone-macro-parentheses, cppcoreguidelines-macro-usage) | ||
|
|
||
| #endif // YSTDLIB_ERROR_HANDLING_ERRORCODE_HPP | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| #include "test-Defs.hpp" | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <system_error> | ||
|
|
||
| template <> | ||
| auto AlwaysSuccessErrorCategory::name() const noexcept -> char const* { | ||
| return cAlwaysSuccessErrorCategoryName.data(); | ||
| } | ||
|
|
||
| template <> | ||
| auto AlwaysSuccessErrorCategory::message(AlwaysSuccessErrorCodeEnum error_enum) const | ||
| -> std::string { | ||
| switch (error_enum) { | ||
| case AlwaysSuccessErrorCodeEnum::Success: | ||
| return std::string{cSuccessErrorMsg}; | ||
| default: | ||
| return std::string{cUnrecognizedErrorCode}; | ||
| } | ||
| } | ||
|
|
||
| template <> | ||
| auto BinaryErrorCategory::name() const noexcept -> char const* { | ||
| return cBinaryTestErrorCategoryName.data(); | ||
| } | ||
|
|
||
| template <> | ||
| auto BinaryErrorCategory::message(BinaryErrorCodeEnum error_enum) const -> std::string { | ||
| switch (error_enum) { | ||
| case BinaryErrorCodeEnum::Success: | ||
| return std::string{cSuccessErrorMsg}; | ||
| case BinaryErrorCodeEnum::Failure: | ||
| return std::string{cFailureErrorMsg}; | ||
| default: | ||
| return std::string{cUnrecognizedErrorCode}; | ||
| } | ||
| } | ||
|
|
||
| template <> | ||
| auto BinaryErrorCategory::equivalent( | ||
| BinaryErrorCodeEnum error_enum, | ||
| std::error_condition const& condition | ||
| ) const noexcept -> bool { | ||
| switch (error_enum) { | ||
| case BinaryErrorCodeEnum::Failure: | ||
| return std::ranges::any_of( | ||
| cFailureConditions.cbegin(), | ||
| cFailureConditions.cend(), | ||
| [&](auto failure_condition) -> bool { return condition == failure_condition; } | ||
| ); | ||
| default: | ||
| return false; | ||
| } | ||
| } |
|
Bill-hbrhbr marked this conversation as resolved.
Outdated
Bill-hbrhbr marked this conversation as resolved.
Outdated
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| #include <array> | ||
| #include <cstdint> | ||
| #include <string_view> | ||
| #include <system_error> | ||
|
|
||
| #include <ystdlib/error_handling/ErrorCode.hpp> | ||
|
|
||
| constexpr std::string_view cAlwaysSuccessErrorCategoryName{"Always Success Error Code"}; | ||
| constexpr std::string_view cBinaryTestErrorCategoryName{"Binary Error Code"}; | ||
| constexpr std::string_view cSuccessErrorMsg{"Success"}; | ||
| constexpr std::string_view cFailureErrorMsg{"Failure"}; | ||
| constexpr std::string_view cUnrecognizedErrorCode{"Unrecognized Error Code"}; | ||
| constexpr std::array cFailureConditions{std::errc::not_connected, std::errc::timed_out}; | ||
| constexpr std::array cNoneFailureConditions{std::errc::broken_pipe, std::errc::address_in_use}; | ||
|
|
||
| enum class AlwaysSuccessErrorCodeEnum : uint8_t { | ||
| Success = 0 | ||
| }; | ||
|
|
||
| enum class BinaryErrorCodeEnum : uint8_t { | ||
| Success = 0, | ||
| Failure | ||
| }; | ||
|
|
||
| using AlwaysSuccessErrorCode = ystdlib::error_handling::ErrorCode<AlwaysSuccessErrorCodeEnum>; | ||
| using AlwaysSuccessErrorCategory | ||
| = ystdlib::error_handling::ErrorCategory<AlwaysSuccessErrorCodeEnum>; | ||
| using BinaryErrorCode = ystdlib::error_handling::ErrorCode<BinaryErrorCodeEnum>; | ||
| using BinaryErrorCategory = ystdlib::error_handling::ErrorCategory<BinaryErrorCodeEnum>; | ||
|
Bill-hbrhbr marked this conversation as resolved.
Outdated
|
||
|
|
||
| YSTDLIB_ERROR_HANDLING_MARK_AS_ERROR_CODE_ENUM(AlwaysSuccessErrorCodeEnum); | ||
| YSTDLIB_ERROR_HANDLING_MARK_AS_ERROR_CODE_ENUM(BinaryErrorCodeEnum); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #include <algorithm> | ||
| #include <array> | ||
| #include <string_view> | ||
| #include <system_error> | ||
|
|
||
| #include <ystdlib/error_handling/ErrorCode.hpp> | ||
|
|
||
| #include <catch2/catch_test_macros.hpp> | ||
|
|
||
| #include "test-Defs.hpp" | ||
|
|
||
| TEST_CASE("test_error_code", "[error_handling][ErrorCode]") { | ||
| // Test error codes within the same error category | ||
| BinaryErrorCode const success{BinaryErrorCodeEnum::Success}; | ||
| std::error_code const success_error_code{success}; | ||
| REQUIRE((success == success_error_code)); | ||
| REQUIRE((cSuccessErrorMsg == success_error_code.message())); | ||
| REQUIRE((BinaryErrorCode::get_category() == success_error_code.category())); | ||
| REQUIRE((cBinaryTestErrorCategoryName == success_error_code.category().name())); | ||
|
|
||
| BinaryErrorCode const failure{BinaryErrorCodeEnum::Failure}; | ||
| std::error_code const failure_error_code{failure}; | ||
| REQUIRE((failure == failure_error_code)); | ||
| REQUIRE((cFailureErrorMsg == failure_error_code.message())); | ||
| REQUIRE((BinaryErrorCode::get_category() == failure_error_code.category())); | ||
| REQUIRE((cBinaryTestErrorCategoryName == failure_error_code.category().name())); | ||
|
|
||
| REQUIRE((success_error_code != failure_error_code)); | ||
| REQUIRE((success_error_code.category() == failure_error_code.category())); | ||
|
|
||
| AlwaysSuccessErrorCode const always_success{AlwaysSuccessErrorCodeEnum::Success}; | ||
| std::error_code const always_success_error_code{always_success}; | ||
| REQUIRE((always_success_error_code == always_success)); | ||
| REQUIRE((cSuccessErrorMsg == always_success_error_code.message())); | ||
| REQUIRE((AlwaysSuccessErrorCode::get_category() == always_success_error_code.category())); | ||
| REQUIRE((cAlwaysSuccessErrorCategoryName == always_success_error_code.category().name())); | ||
|
|
||
| // Compare error codes from different error category | ||
| // Error codes that have the same value or message won't be the same with each other if they are | ||
| // from different error categories. | ||
| REQUIRE((success_error_code.value() == always_success_error_code.value())); | ||
| REQUIRE((success_error_code.message() == always_success_error_code.message())); | ||
| REQUIRE((success_error_code.category() != always_success_error_code.category())); | ||
| REQUIRE((success_error_code != always_success_error_code)); | ||
| REQUIRE((AlwaysSuccessErrorCode{AlwaysSuccessErrorCodeEnum::Success} != success_error_code)); | ||
| REQUIRE((BinaryErrorCode{BinaryErrorCodeEnum::Success} != always_success_error_code)); | ||
| } | ||
|
|
||
| TEST_CASE("test_error_code_failure_condition", "[error_handling][ErrorCode]") { | ||
| std::error_code const failure_error_code{BinaryErrorCode{BinaryErrorCodeEnum::Failure}}; | ||
| std::ranges::for_each( | ||
| cFailureConditions.cbegin(), | ||
| cFailureConditions.cend(), | ||
| [&](auto failure_condition) { REQUIRE((failure_error_code == failure_condition)); } | ||
| ); | ||
| std::ranges::for_each( | ||
| cNoneFailureConditions.cbegin(), | ||
| cNoneFailureConditions.cend(), | ||
| [&](auto none_failure_condition) { | ||
| REQUIRE((failure_error_code != none_failure_condition)); | ||
| } | ||
| ); | ||
| } |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.