// Copyright (c) 2022 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_UTIL_RESULT_H #define BITCOIN_UTIL_RESULT_H #include #include #include namespace util { struct Error { bilingual_str message; }; //! The util::Result class provides a standard way for functions to return //! either error messages or result values. //! //! It is intended for high-level functions that need to report error strings to //! end users. Lower-level functions that don't need this error-reporting and //! only need error-handling should avoid util::Result and instead use standard //! classes like std::optional, std::variant, and std::tuple, or custom structs //! and enum types to return function results. //! //! Usage examples can be found in \example ../test/result_tests.cpp, but in //! general code returning `util::Result` values is very similar to code //! returning `std::optional` values. Existing functions returning //! `std::optional` can be updated to return `util::Result` and return //! error strings usually just replacing `return std::nullopt;` with `return //! util::Error{error_string};`. template class Result { private: std::variant m_variant; template friend bilingual_str ErrorString(const Result& result); public: Result(T obj) : m_variant{std::in_place_index_t<1>{}, std::move(obj)} {} Result(Error error) : m_variant{std::in_place_index_t<0>{}, std::move(error.message)} {} //! std::optional methods, so functions returning optional can change to //! return Result with minimal changes to existing code, and vice versa. bool has_value() const noexcept { return m_variant.index() == 1; } const T& value() const LIFETIMEBOUND { assert(has_value()); return std::get<1>(m_variant); } T& value() LIFETIMEBOUND { assert(has_value()); return std::get<1>(m_variant); } template T value_or(U&& default_value) const& { return has_value() ? value() : std::forward(default_value); } template T value_or(U&& default_value) && { return has_value() ? std::move(value()) : std::forward(default_value); } explicit operator bool() const noexcept { return has_value(); } const T* operator->() const LIFETIMEBOUND { return &value(); } const T& operator*() const LIFETIMEBOUND { return value(); } T* operator->() LIFETIMEBOUND { return &value(); } T& operator*() LIFETIMEBOUND { return value(); } }; template bilingual_str ErrorString(const Result& result) { return result ? bilingual_str{} : std::get<0>(result.m_variant); } } // namespace util #endif // BITCOIN_UTIL_RESULT_H