aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-07-09 16:14:02 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-07-09 16:14:02 +0200
commit294db0b50e917602f9468f3ccfa0a83ef50f22c0 (patch)
tree80c26219989531a3b4630b48e869876e03edf62a
parent7469985a4ee794ecc736de12f7d768f1746419c0 (diff)
downloadkernel-294db0b50e917602f9468f3ccfa0a83ef50f22c0.tar.xz
kernel-294db0b50e917602f9468f3ccfa0a83ef50f22c0.zip
kstd: begin implementation of system_error
-rw-r--r--libs/kstd/kstd/system_error.hpp363
-rw-r--r--libs/kstd/kstd/system_error.tests.cpp55
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);
+ }
+ }
+ }
+}