/************************************************************************* * * Copyright 2021 Realm Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **************************************************************************/ #pragma once #include #include "realm/status.hpp" #include "realm/util/assert.hpp" #include "realm/util/features.h" #include "realm/util/optional.hpp" namespace realm { template class StatusWith; template constexpr bool is_status_with = false; template constexpr bool is_status_with> = true; template constexpr bool is_status_or_status_with = std::is_same_v || is_status_with; 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 REALM_NODISCARD StatusWith { static_assert(!is_status_or_status_with, "StatusWith and StatusWith> are banned."); public: using value_type = T; template , int> = 0> StatusWith(ErrorCodes::Error code, Reason reason) : m_status(code, reason) { } StatusWith(Status status) : m_status(std::move(status)) { } StatusWith(T value) : m_status(Status::OK()) , m_value(std::move(value)) { } bool is_ok() const { return m_status.is_ok(); } const T& get_value() const { REALM_ASSERT_DEBUG(is_ok()); REALM_ASSERT_RELEASE(m_value); return *m_value; } T& get_value() { REALM_ASSERT_DEBUG(is_ok()); REALM_ASSERT_RELEASE(m_value); return *m_value; } const Status& get_status() const { return m_status; } private: Status m_status; util::Optional m_value; }; template StatusWith make_status_with(Args&&... args) { return StatusWith{T(std::forward(args)...)}; } template auto operator<<(std::ostream& stream, const StatusWith& sw) -> decltype(stream << sw.get_value()) // SFINAE on T streamability. { if (sw.is_ok()) return stream << sw.get_value(); return stream << sw.get_status(); } // // EqualityComparable(StatusWith, T). Intentionally not providing an ordering relation. // template bool operator==(const StatusWith& sw, const T& val) { return sw.is_ok() && sw.get_value() == val; } template bool operator==(const T& val, const StatusWith& sw) { return sw.is_ok() && val == sw.get_value(); } 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.get_status() == status; } template bool operator==(const Status& status, const StatusWith& sw) { return status == sw.get_status(); } 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.get_status() == code; } template bool operator==(const ErrorCodes::Error code, const StatusWith& sw) { return code == sw.get_status(); } 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 realm