/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #include #include #include #include #include "mongo/base/static_assert.h" #include "mongo/base/status.h" #include "mongo/bson/util/builder_fwd.h" #include "mongo/platform/compiler.h" #define MONGO_ALLOW_INCLUDE_INVARIANT_H #include "mongo/util/invariant.h" #undef MONGO_ALLOW_INCLUDE_INVARIANT_H namespace mongo { template class StatusWith; template inline constexpr bool isStatusWith = false; template inline constexpr bool isStatusWith> = true; template inline constexpr bool isStatusOrStatusWith = std::is_same_v || isStatusWith; template using StatusOrStatusWith = std::conditional_t, Status, StatusWith>; /** * StatusWith is used to return an error or a value. * This class is designed to make exception-free code cleaner by not needing as many out * parameters. * * Example: * StatusWith fib( int n ) { * if ( n < 0 ) * return StatusWith( ErrorCodes::BadValue, "parameter to fib has to be >= 0" ); * if ( n <= 1 ) return StatusWith( 1 ); * StatusWith a = fib( n - 1 ); * StatusWith b = fib( n - 2 ); * if ( !a.isOK() ) return a; * if ( !b.isOK() ) return b; * return StatusWith( a.getValue() + b.getValue() ); * } */ template class MONGO_WARN_UNUSED_RESULT_CLASS StatusWith { private: MONGO_STATIC_ASSERT_MSG(!isStatusOrStatusWith, "StatusWith and StatusWith> are banned."); // `TagTypeBase` is used as a base for the `TagType` type, to prevent it from being an // aggregate. struct TagTypeBase { protected: TagTypeBase() = default; }; // `TagType` is used as a placeholder type in parameter lists for `enable_if` clauses. They // have to be real parameters, not template parameters, due to MSVC limitations. class TagType : TagTypeBase { TagType() = default; friend StatusWith; }; public: using value_type = T; /** * For the error case. * As with the `Status` constructors, `reason` can be `std::string` or * anything that can construct one (e.g. `StringData`, `str::stream`). */ MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, std::string reason) : _status(code, std::move(reason)) {} template , int> = 0> MONGO_COMPILER_COLD_FUNCTION StatusWith(ErrorCodes::Error code, Reason&& reason) : StatusWith(code, std::string{std::forward(reason)}) {} /** * for the error case */ MONGO_COMPILER_COLD_FUNCTION StatusWith(Status status) : _status(std::move(status)) { dassert(!isOK()); } /** * for the OK case */ StatusWith(T t) : _status(Status::OK()), _t(std::move(t)) {} template StatusWith(Alien&& alien, typename std::enable_if_t::value, TagType> = makeTag(), typename std::enable_if_t::value, TagType> = makeTag()) : StatusWith(static_cast(std::forward(alien))) {} template StatusWith(StatusWith alien, typename std::enable_if_t::value, TagType> = makeTag(), typename std::enable_if_t::value, TagType> = makeTag()) : _status(std::move(alien.getStatus())) { if (alien.isOK()) this->_t = std::move(alien.getValue()); } const T& getValue() const { dassert(isOK()); return *_t; } T& getValue() { dassert(isOK()); return *_t; } const Status& getStatus() const { return _status; } bool isOK() const { return _status.isOK(); } /** * For any type U returned by a function f, transform creates a StatusWith by either applying * the function to the _t member or forwarding the _status. This is the lvalue overload. */ template StatusWith> transform(F&& f) & { if (_t) return {std::forward(f)(*_t)}; else return {_status}; } /** * For any type U returned by a function f, transform creates a StatusWith by either applying * the function to the _t member or forwarding the _status. This is the const overload. */ template StatusWith> transform(F&& f) const& { if (_t) return {std::forward(f)(*_t)}; else return {_status}; } /** * For any type U returned by a function f, transform creates a StatusWith by either applying * the function to the _t member or forwarding the _status. This is the rvalue overload. */ template StatusWith> transform(F&& f) && { if (_t) return {std::forward(f)(*std::move(_t))}; else return {std::move(_status)}; } /** * For any type U returned by a function f, transform creates a StatusWith by either applying * the function to the _t member or forwarding the _status. This is the const rvalue overload. */ template StatusWith> transform(F&& f) const&& { if (_t) return {std::forward(f)(*std::move(_t))}; else return {std::move(_status)}; } /** * For any type U returned inside a StatusWith by a function f, andThen directly produces a * StatusWith by applying the function to the _t member or creates one by forwarding the * _status. andThen performs the same function as transform but for a function f with a return * type of StatusWith. This is the lvalue overload. */ template StatusWith::value_type> andThen(F&& f) & { if (_t) return {std::forward(f)(*_t)}; else return {_status}; } /** * For any type U returned inside a StatusWith by a function f, andThen directly produces a * StatusWith by applying the function to the _t member or creates one by forwarding the * _status. andThen performs the same function as transform but for a function f with a return * type of StatusWith. This is the const overload. */ template StatusWith::value_type> andThen(F&& f) const& { if (_t) return {std::forward(f)(*_t)}; else return {_status}; } /** * For any type U returned inside a StatusWith by a function f, andThen directly produces a * StatusWith by applying the function to the _t member or creates one by forwarding the * _status. andThen performs the same function as transform but for a function f with a return * type of StatusWith. This is the rvalue overload. */ template StatusWith::value_type> andThen(F&& f) && { if (_t) return {std::forward(f)(*std::move(_t))}; else return {std::move(_status)}; } /** * For any type U returned inside a StatusWith by a function f, andThen directly produces a * StatusWith by applying the function to the _t member or creates one by forwarding the * _status. andThen performs the same function as transform but for a function f with a return * type of StatusWith. This is the const rvalue overload. */ template StatusWith::value_type> andThen(F&& f) const&& { if (_t) return {std::forward(f)(*std::move(_t))}; else return {std::move(_status)}; } /** * This method is a transitional tool, to facilitate transition to compile-time enforced status * checking. * * NOTE: DO NOT ADD NEW CALLS TO THIS METHOD. This method serves the same purpose as * `.getStatus().ignore()`; however, it indicates a situation where the code that presently * ignores a status code has not been audited for correctness. This method will be removed at * some point. If you encounter a compiler error from ignoring the result of a `StatusWith` * returning function be sure to check the return value, or deliberately ignore the return * value. The function is named to be auditable independently from unaudited `Status` ignore * cases. */ void status_with_transitional_ignore() && noexcept {}; void status_with_transitional_ignore() const& noexcept = delete; private: // The `TagType` type cannot be constructed as a default function-parameter in Clang. So we use // a static member function that initializes that default parameter. static TagType makeTag() { return {}; } Status _status; boost::optional _t; }; template auto operator<<(std::ostream& stream, const StatusWith& sw) -> decltype(stream << sw.getValue()) // SFINAE on T streamability. { if (sw.isOK()) return stream << sw.getValue(); return stream << sw.getStatus(); } template auto operator<<(StringBuilderImpl& stream, const StatusWith& sw) -> decltype(stream << sw.getValue()) // SFINAE on T streamability. { if (sw.isOK()) return stream << sw.getValue(); return stream << sw.getStatus(); } // // EqualityComparable(StatusWith, T). Intentionally not providing an ordering relation. // template bool operator==(const StatusWith& sw, const T& val) { return sw.isOK() && sw.getValue() == val; } template bool operator==(const T& val, const StatusWith& sw) { return sw.isOK() && val == sw.getValue(); } template bool operator!=(const StatusWith& sw, const T& val) { return !(sw == val); } template bool operator!=(const T& val, const StatusWith& sw) { return !(val == sw); } // // EqualityComparable(StatusWith, Status) // template bool operator==(const StatusWith& sw, const Status& status) { return sw.getStatus() == status; } template bool operator==(const Status& status, const StatusWith& sw) { return status == sw.getStatus(); } template bool operator!=(const StatusWith& sw, const Status& status) { return !(sw == status); } template bool operator!=(const Status& status, const StatusWith& sw) { return !(status == sw); } // // EqualityComparable(StatusWith, ErrorCode) // template bool operator==(const StatusWith& sw, const ErrorCodes::Error code) { return sw.getStatus() == code; } template bool operator==(const ErrorCodes::Error code, const StatusWith& sw) { return code == sw.getStatus(); } template bool operator!=(const StatusWith& sw, const ErrorCodes::Error code) { return !(sw == code); } template bool operator!=(const ErrorCodes::Error code, const StatusWith& sw) { return !(code == sw); } } // namespace mongo