From 14ab32dc57a5d6877223b065138295531affe5c4 Mon Sep 17 00:00:00 2001 From: Max SCHMELLER Date: Tue, 21 May 2024 16:37:06 +0900 Subject: [PATCH] fix(expected): make error behavior clearer, add doc comments --- .../include/nebula_common/util/expected.hpp | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/nebula_common/include/nebula_common/util/expected.hpp b/nebula_common/include/nebula_common/util/expected.hpp index 4fc35b3fa..6a441db70 100644 --- a/nebula_common/include/nebula_common/util/expected.hpp +++ b/nebula_common/include/nebula_common/util/expected.hpp @@ -7,6 +7,17 @@ namespace nebula namespace util { +struct bad_expected_access : public std::exception { + bad_expected_access(const std::string & msg) : std::exception(), msg_(msg) {} + + const char* what() const noexcept override { + return msg_.c_str(); + } + +private: + const std::string msg_; +}; + /// @brief A poor man's backport of C++23's std::expected. /// /// At any given time, holds exactly one of: expected value, or error value. @@ -19,23 +30,46 @@ namespace util template struct expected { + /// @brief Whether the expected instance holds a value (as opposed to an error). + /// Call this before trying to access values via `value()`. + /// @return True if a value is contained, false if an error is contained bool has_value() { return std::holds_alternative(value_); } - T value() { return std::get(value_); } + /// @brief Retrieve the value, or throw `bad_expected_access` if an error is contained. + /// @return The value of type `T` + T value() { + if (!has_value()) { + throw bad_expected_access("value() called but containing error"); + } + return std::get(value_); + } + /// @brief Return the contained value, or, if an error is contained, return the given `default_` instead. + /// @return The contained value, if any, else `default_` T value_or(const T & default_) { if (has_value()) return value(); return default_; } + /// @brief Return the contained value, or, if an error is contained, throw `runtime_error(error_msg)`. + /// @return The contained value if no error is thrown T value_or_throw(const std::string & error_msg) { if (has_value()) return value(); throw std::runtime_error(error_msg); } - E error() { return std::get(value_); } + /// @brief Retrieve the error, or throw `bad_expected_access` if a value is contained. + /// @return The error of type `E` + E error() { + if (has_value()) { + throw bad_expected_access("error() called but containing value"); + } + return std::get(value_); + } + /// @brief Return the contained error, or, if a value is contained, return the given `default_` instead. + /// @return The contained error, if any, else `default_` E error_or(const E & default_) { if (!has_value()) return error(); return default_;