diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-07-09 16:14:02 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-07-09 16:14:02 +0200 |
| commit | 294db0b50e917602f9468f3ccfa0a83ef50f22c0 (patch) | |
| tree | 80c26219989531a3b4630b48e869876e03edf62a /libs | |
| parent | 7469985a4ee794ecc736de12f7d768f1746419c0 (diff) | |
| download | kernel-294db0b50e917602f9468f3ccfa0a83ef50f22c0.tar.xz kernel-294db0b50e917602f9468f3ccfa0a83ef50f22c0.zip | |
kstd: begin implementation of system_error
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/kstd/kstd/system_error.hpp | 363 | ||||
| -rw-r--r-- | libs/kstd/kstd/system_error.tests.cpp | 55 |
2 files changed, 418 insertions, 0 deletions
diff --git a/libs/kstd/kstd/system_error.hpp b/libs/kstd/kstd/system_error.hpp new file mode 100644 index 00000000..221347a8 --- /dev/null +++ b/libs/kstd/kstd/system_error.hpp @@ -0,0 +1,363 @@ +#ifndef KSTD_SYSTEM_ERROR_HPP +#define KSTD_SYSTEM_ERROR_HPP + +#include <compare> +#include <cstdint> +#include <functional> +#include <string_view> +#include <type_traits> + +namespace kstd +{ + + struct error_condition; + struct error_code; + + template<typename CandidateType> + requires(std::is_enum_v<CandidateType>) + struct is_error_condition_enum : std::false_type + { + }; + + template<typename CandidateType> + constexpr auto inline is_error_condition_enum_v = is_error_condition_enum<CandidateType>::value; + + template<typename CandidateType> + concept error_condition_enum = std::is_enum_v<CandidateType> && is_error_condition_enum_v<CandidateType>; + + template<typename CandidateType> + requires(std::is_enum_v<CandidateType>) + struct is_error_code_enum : std::false_type + { + }; + + template<typename CandidateType> + constexpr auto inline is_error_code_enum_v = is_error_code_enum<CandidateType>::value; + + template<typename CandidateType> + concept error_code_enum = std::is_enum_v<CandidateType> && is_error_code_enum_v<CandidateType>; + + struct error_category + { + constexpr error_category() noexcept = default; + + error_category(error_category const &) = delete; + + constexpr virtual ~error_category() = default; + + auto operator=(error_category const &) -> error_category & = delete; + + [[nodiscard]] constexpr auto virtual name() const noexcept -> std::string_view = 0; + + [[nodiscard]] constexpr auto virtual default_error_condition(int value) const noexcept -> error_condition; + + [[nodiscard]] constexpr auto virtual equivalent(int value, error_condition const & other) const noexcept -> bool; + + [[nodiscard]] constexpr auto virtual equivalent(error_code const &, int condition) const noexcept -> bool; + + [[nodiscard]] constexpr auto virtual message(int value) const noexcept -> std::string_view = 0; + + [[nodiscard]] constexpr auto operator==(error_category const & other) const noexcept -> bool + { + return this == &other; + } + + [[nodiscard]] constexpr auto operator<=>(error_category const & other) const noexcept -> std::strong_ordering + { + return std::compare_three_way{}(this, &other); + } + }; + + struct error_condition + { + constexpr error_condition() noexcept; + + constexpr error_condition(int value, error_category const & category) noexcept + : m_value{value} + , m_category{&category} + {} + + template<error_condition_enum Enum> + constexpr error_condition(Enum value) noexcept + : error_condition{make_error_condition(value)} + {} + + template<error_condition_enum Enum> + constexpr auto operator=(Enum value) noexcept -> error_condition & + { + *this = make_error_condition(value); + } + + constexpr auto assign(int value, error_category const & category) noexcept -> void + { + m_value = value; + m_category = &category; + } + + constexpr auto clear() noexcept -> void; + + [[nodiscard]] constexpr auto value() const noexcept -> int + { + return m_value; + } + + [[nodiscard]] constexpr auto category() const noexcept -> error_category const & + { + return *m_category; + } + + [[nodiscard]] constexpr auto message() const noexcept -> std::string_view + { + return m_category->message(m_value); + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_value != 0; + } + + [[nodiscard]] constexpr auto friend operator==(error_condition const & lhs, error_condition const & rhs) noexcept + -> bool + { + return lhs.category() == rhs.category() && lhs.value() == rhs.value(); + } + + [[nodiscard]] constexpr auto friend operator<=>(error_condition const & lhs, error_condition const & rhs) noexcept + -> std::strong_ordering + { + if (auto result = lhs.category() <=> rhs.category(); result != std::strong_ordering::equal) + { + return result; + } + + return lhs.value() <=> rhs.value(); + } + + private: + int m_value{}; + error_category const * m_category{}; + }; + + struct error_code + { + constexpr error_code() noexcept; + + constexpr error_code(int value, error_category const & category) noexcept + : m_value{value} + , m_category{&category} + {} + + template<error_code_enum Enum> + constexpr error_code(Enum value) noexcept + : error_code{make_error_condition(value)} + {} + + template<error_code_enum Enum> + constexpr auto operator=(Enum value) noexcept -> error_code & + { + return *this = make_error_code(value); + } + + constexpr auto assign(int value, error_category const & category) noexcept -> void + { + m_value = value; + m_category = &category; + } + + constexpr auto clear() noexcept -> void; + + [[nodiscard]] constexpr auto value() const noexcept -> int + { + return m_value; + } + + [[nodiscard]] constexpr auto category() const noexcept -> error_category const & + { + return *m_category; + } + + [[nodiscard]] constexpr auto default_error_condition() const noexcept -> error_condition + { + return m_category->default_error_condition(m_value); + } + + [[nodiscard]] constexpr auto message() const noexcept -> std::string_view + { + return m_category->message(m_value); + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_value != 0; + } + + [[nodiscard]] constexpr auto friend operator==(error_code const & lhs, error_code const & rhs) noexcept -> bool + { + return lhs.category() == rhs.category() && lhs.value() == rhs.value(); + } + + [[nodiscard]] constexpr auto friend operator<=>(error_code const & lhs, error_code const & rhs) noexcept + -> std::strong_ordering + { + if (auto result = lhs.category() <=> rhs.category(); result != std::strong_ordering::equal) + { + return result; + } + + return lhs.value() <=> rhs.value(); + } + + private: + int m_value{}; + error_category const * m_category{}; + }; + + enum struct errc : std::uint32_t + { + success = 0, + no_such_file_or_directory, + io_error, + not_enough_memory, + permission_denied, + device_or_resource_busy, + invalid_argument, + not_supported, + }; + + template<> + struct is_error_condition_enum<errc> : std::true_type + { + }; + + namespace bits + { + struct generic_category_t final : error_category + { + [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override + { + return "generic"; + } + + [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override + { + switch (static_cast<errc>(value)) + { + case errc::success: + return "success"; + case errc::no_such_file_or_directory: + return "no such file or directory"; + case errc::io_error: + return "input/output error"; + case errc::not_enough_memory: + return "not enough memory"; + case errc::permission_denied: + return "permission denied"; + case errc::device_or_resource_busy: + return "device or resource busy"; + case errc::invalid_argument: + return "invalid argument"; + case errc::not_supported: + return "not supported"; + default: + return "unknown generic error"; + } + } + } constexpr inline generic_category_instance{}; + + struct system_category_t final : error_category + { + [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override + { + return "system"; + } + + [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override + { + switch (static_cast<errc>(value)) + { + case errc::success: + return "success"; + case errc::no_such_file_or_directory: + return "no such file or directory"; + case errc::io_error: + return "input/output error"; + case errc::not_enough_memory: + return "not enough memory"; + case errc::permission_denied: + return "permission denied"; + case errc::device_or_resource_busy: + return "device or resource busy"; + case errc::invalid_argument: + return "invalid argument"; + case errc::not_supported: + return "not supported"; + default: + return "unknown system error"; + } + } + } constexpr inline system_category_instance{}; + } // namespace bits + + constexpr auto inline generic_category() noexcept -> error_category const & + { + return bits::generic_category_instance; + } + + constexpr auto inline system_category() noexcept -> error_category const & + { + return bits::system_category_instance; + } + + constexpr auto inline error_category::default_error_condition(int value) const noexcept -> error_condition + { + return error_condition{value, *this}; + } + + constexpr auto inline error_category::equivalent(int value, error_condition const & other) const noexcept -> bool + { + return default_error_condition(value) == other; + } + + constexpr auto error_category::equivalent(error_code const & code, int condition) const noexcept -> bool + { + return *this == code.category() && code.value() == condition; + } + + constexpr error_condition::error_condition() noexcept + : error_condition{0, generic_category()} + {} + + constexpr auto error_condition::clear() noexcept -> void + { + m_value = 0; + m_category = &generic_category(); + } + + constexpr auto operator==(error_code const & code, error_condition const & condition) noexcept -> bool + { + return code.category().equivalent(code.value(), condition) || + condition.category().equivalent(code, condition.value()); + } + + constexpr error_code::error_code() noexcept + : error_code{0, system_category()} {}; + + constexpr auto error_code::clear() noexcept -> void + { + m_value = 0; + m_category = &system_category(); + } + + [[nodiscard]] constexpr auto inline make_error_code(errc const value) noexcept -> error_code + { + return {static_cast<int>(value), generic_category()}; + } + + [[nodiscard]] constexpr auto inline make_error_condition(errc const value) noexcept -> error_code + { + return {static_cast<int>(value), generic_category()}; + } + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/system_error.tests.cpp b/libs/kstd/kstd/system_error.tests.cpp new file mode 100644 index 00000000..12f952b3 --- /dev/null +++ b/libs/kstd/kstd/system_error.tests.cpp @@ -0,0 +1,55 @@ +#include <kstd/system_error.hpp> + +#include <catch2/catch_test_macros.hpp> + +SCENARIO("Error condition initialization and construction", "[kstd][system_error]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto error = kstd::error_condition{}; + + THEN("the category is equal to generic_category") + { + REQUIRE(error.category() == kstd::generic_category()); + } + + THEN("the value is 0") + { + REQUIRE(error.value() == 0); + } + + THEN("conversion to bool yields false") + { + REQUIRE_FALSE(error); + } + } + } +} + +SCENARIO("Error code initialization and construction", "[kstd][system_error]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto error = kstd::error_code{}; + + THEN("the category is equal to system_category") + { + REQUIRE(error.category() == kstd::system_category()); + } + + THEN("the value is 0") + { + REQUIRE(error.value() == 0); + } + + THEN("conversion to bool yields false") + { + REQUIRE_FALSE(error); + } + } + } +} |
