From e16ceec64702bd8f2b1b3fcdd888cfa6c42b5a28 Mon Sep 17 00:00:00 2001 From: crosstyan Date: Tue, 10 Mar 2026 23:54:22 +0800 Subject: [PATCH] refactor(core): use std error_code conditions --- include/cvmmap_streamer/core/status.hpp | 238 +++++++++++++++++++----- src/protocol/rtmp_output.cpp | 20 +- 2 files changed, 210 insertions(+), 48 deletions(-) diff --git a/include/cvmmap_streamer/core/status.hpp b/include/cvmmap_streamer/core/status.hpp index bcf125c..a2d4b92 100644 --- a/include/cvmmap_streamer/core/status.hpp +++ b/include/cvmmap_streamer/core/status.hpp @@ -3,13 +3,13 @@ #include #include #include +#include +#include #include namespace cvmmap_streamer { -using error_t = int; - -enum class ErrorCode : error_t { +enum class ErrorCode : int { Ok = 0, InvalidArgument, InvalidConfig, @@ -28,6 +28,90 @@ enum class ErrorCode : error_t { Internal, }; +[[nodiscard]] +inline std::string_view error_code_name(ErrorCode code) { + switch (code) { + case ErrorCode::Ok: + return "ok"; + case ErrorCode::InvalidArgument: + return "invalid_argument"; + case ErrorCode::InvalidConfig: + return "invalid_config"; + case ErrorCode::Unsupported: + return "unsupported"; + case ErrorCode::NotReady: + return "not_ready"; + case ErrorCode::BackendUnavailable: + return "backend_unavailable"; + case ErrorCode::AllocationFailed: + return "allocation_failed"; + case ErrorCode::Io: + return "io"; + case ErrorCode::Network: + return "network"; + case ErrorCode::Protocol: + return "protocol"; + case ErrorCode::Encoder: + return "encoder"; + case ErrorCode::ExternalLibrary: + return "external_library"; + case ErrorCode::Serialization: + return "serialization"; + case ErrorCode::ChildProcess: + return "child_process"; + case ErrorCode::EndOfStream: + return "end_of_stream"; + case ErrorCode::Internal: + return "internal"; + default: + return "unknown"; + } +} + +class StreamerErrorCategory final : public std::error_category { +public: + [[nodiscard]] + const char *name() const noexcept override { + return "cvmmap_streamer"; + } + + [[nodiscard]] + std::string message(int code) const override { + return std::string(error_code_name(static_cast(code))); + } + + [[nodiscard]] + std::error_condition default_error_condition(int code) const noexcept override { + switch (static_cast(code)) { + case ErrorCode::Ok: + return {}; + case ErrorCode::InvalidArgument: + return std::make_error_condition(std::errc::invalid_argument); + case ErrorCode::Unsupported: + return std::make_error_condition(std::errc::not_supported); + case ErrorCode::AllocationFailed: + return std::make_error_condition(std::errc::not_enough_memory); + case ErrorCode::Io: + return std::make_error_condition(std::errc::io_error); + case ErrorCode::Protocol: + return std::make_error_condition(std::errc::protocol_error); + default: + return std::error_condition(code, *this); + } + } +}; + +[[nodiscard]] +inline const std::error_category &streamer_error_category() noexcept { + static const StreamerErrorCategory category{}; + return category; +} + +[[nodiscard]] +inline std::error_code make_error_code(ErrorCode code) noexcept { + return {static_cast(code), streamer_error_category()}; +} + inline constexpr ErrorCode ERR_OK = ErrorCode::Ok; inline constexpr ErrorCode ERR_INVALID_ARGUMENT = ErrorCode::InvalidArgument; inline constexpr ErrorCode ERR_INVALID_CONFIG = ErrorCode::InvalidConfig; @@ -46,7 +130,7 @@ inline constexpr ErrorCode ERR_END_OF_STREAM = ErrorCode::EndOfStream; inline constexpr ErrorCode ERR_INTERNAL = ErrorCode::Internal; struct Error { - ErrorCode code{ERR_OK}; + std::error_code code{make_error_code(ERR_OK)}; std::string detail{}; }; @@ -56,7 +140,7 @@ using Result = std::expected; using Status = Result; [[nodiscard]] -inline std::unexpected unexpected_error(ErrorCode code, std::string detail = {}) { +inline std::unexpected unexpected_error(std::error_code code, std::string detail = {}) { return std::unexpected(Error{ .code = code, .detail = std::move(detail), @@ -64,51 +148,117 @@ inline std::unexpected unexpected_error(ErrorCode code, std::string detai } [[nodiscard]] -inline std::string_view error_code_name(ErrorCode code) { - switch (code) { - case ERR_OK: - return "ok"; - case ERR_INVALID_ARGUMENT: - return "invalid_argument"; - case ERR_INVALID_CONFIG: - return "invalid_config"; - case ERR_UNSUPPORTED: - return "unsupported"; - case ERR_NOT_READY: - return "not_ready"; - case ERR_BACKEND_UNAVAILABLE: - return "backend_unavailable"; - case ERR_ALLOCATION_FAILED: - return "allocation_failed"; - case ERR_IO: - return "io"; - case ERR_NETWORK: - return "network"; - case ERR_PROTOCOL: - return "protocol"; - case ERR_ENCODER: - return "encoder"; - case ERR_EXTERNAL_LIBRARY: - return "external_library"; - case ERR_SERIALIZATION: - return "serialization"; - case ERR_CHILD_PROCESS: - return "child_process"; - case ERR_END_OF_STREAM: - return "end_of_stream"; - case ERR_INTERNAL: - return "internal"; - default: - return "unknown"; - } +inline std::unexpected unexpected_error(ErrorCode code, std::string detail = {}) { + return unexpected_error(make_error_code(code), std::move(detail)); +} + +[[nodiscard]] +inline bool has_error_code(const std::error_code &code, ErrorCode expected) noexcept { + return code == make_error_code(expected); +} + +[[nodiscard]] +inline bool has_error_code(const Error &error, ErrorCode expected) noexcept { + return has_error_code(error.code, expected); +} + +[[nodiscard]] +inline bool has_error_condition(const std::error_code &code, std::errc condition) noexcept { + return code == condition; +} + +[[nodiscard]] +inline bool has_error_condition(const Error &error, std::errc condition) noexcept { + return has_error_condition(error.code, condition); +} + +[[nodiscard]] +inline bool is_invalid_argument_error(const std::error_code &code) noexcept { + return has_error_condition(code, std::errc::invalid_argument); +} + +[[nodiscard]] +inline bool is_invalid_argument_error(const Error &error) noexcept { + return is_invalid_argument_error(error.code); +} + +[[nodiscard]] +inline bool is_unsupported_error(const std::error_code &code) noexcept { + return has_error_condition(code, std::errc::not_supported); +} + +[[nodiscard]] +inline bool is_unsupported_error(const Error &error) noexcept { + return is_unsupported_error(error.code); +} + +[[nodiscard]] +inline bool is_allocation_failed_error(const std::error_code &code) noexcept { + return has_error_condition(code, std::errc::not_enough_memory); +} + +[[nodiscard]] +inline bool is_allocation_failed_error(const Error &error) noexcept { + return is_allocation_failed_error(error.code); +} + +[[nodiscard]] +inline bool is_io_error(const std::error_code &code) noexcept { + return has_error_condition(code, std::errc::io_error); +} + +[[nodiscard]] +inline bool is_io_error(const Error &error) noexcept { + return is_io_error(error.code); +} + +[[nodiscard]] +inline bool is_protocol_error(const std::error_code &code) noexcept { + return has_error_condition(code, std::errc::protocol_error); +} + +[[nodiscard]] +inline bool is_protocol_error(const Error &error) noexcept { + return is_protocol_error(error.code); +} + +[[nodiscard]] +inline bool is_not_ready_error(const std::error_code &code) noexcept { + return has_error_code(code, ERR_NOT_READY); +} + +[[nodiscard]] +inline bool is_not_ready_error(const Error &error) noexcept { + return is_not_ready_error(error.code); +} + +[[nodiscard]] +inline bool is_end_of_stream_error(const std::error_code &code) noexcept { + return has_error_code(code, ERR_END_OF_STREAM); +} + +[[nodiscard]] +inline bool is_end_of_stream_error(const Error &error) noexcept { + return is_end_of_stream_error(error.code); } [[nodiscard]] inline std::string format_error(const Error &error) { + const auto message = error.code.message(); if (error.detail.empty()) { - return std::string(error_code_name(error.code)); + return message; } - return std::string(error_code_name(error.code)) + ": " + error.detail; + if (error.code.category() == streamer_error_category()) { + return message + ": " + error.detail; + } + return error.detail + ": " + message; } } + +namespace std { + +template <> +struct is_error_code_enum<::cvmmap_streamer::ErrorCode> : true_type {}; + +} diff --git a/src/protocol/rtmp_output.cpp b/src/protocol/rtmp_output.cpp index b5fc211..31a8d6c 100644 --- a/src/protocol/rtmp_output.cpp +++ b/src/protocol/rtmp_output.cpp @@ -213,7 +213,10 @@ Status write_all(int fd, std::span bytes) { if (errno == EINTR) { continue; } - return unexpected_error(ERR_IO, std::strerror(errno)); + const auto error = errno; + return unexpected_error( + std::error_code(error, std::generic_category()), + "failed to write to ffmpeg process stdin"); } if (result == 0) { return unexpected_error(ERR_IO, "short write to ffmpeg process stdin"); @@ -530,14 +533,20 @@ private: Result spawn_session(const std::string &url) const { int stdin_pipe[2]{-1, -1}; if (pipe(stdin_pipe) != 0) { - return unexpected_error(ERR_IO, "failed to create ffmpeg stdin pipe: " + std::string(std::strerror(errno))); + const auto error = errno; + return unexpected_error( + std::error_code(error, std::generic_category()), + "failed to create ffmpeg stdin pipe"); } const auto child = fork(); if (child < 0) { + const auto error = errno; close(stdin_pipe[0]); close(stdin_pipe[1]); - return unexpected_error(ERR_CHILD_PROCESS, "failed to fork ffmpeg child: " + std::string(std::strerror(errno))); + return unexpected_error( + std::error_code(error, std::generic_category()), + "failed to fork ffmpeg child"); } if (child == 0) { @@ -626,7 +635,10 @@ private: return {}; } if (wait_result < 0) { - return unexpected_error(ERR_CHILD_PROCESS, "failed to poll ffmpeg child: " + std::string(std::strerror(errno))); + const auto error = errno; + return unexpected_error( + std::error_code(error, std::generic_category()), + "failed to poll ffmpeg child"); } if (session.stdin_fd >= 0) {