// Copyright (C) 2018 The Android Open Source Project // // 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 "base/Optional.h" // Result - a template class to store either a result or error, inspired // by Rust. // // T is the successful result type, and may be void. // E is the error result type. // // To return success, return Ok(value) or Ok(). // To return error, return Err(value). // // Usage in APIs: // // class MyError1 { Error1, Error2 }; // typedef Result MyResult; // // MyResult function() { // if (good()) return Ok(); // else return Err(MyError::Error1); // } // // Checking results: // // if (result.ok()) { auto value = *result.ok(); } // no error // return result; // propagate result as-is // RETURN_IF_ERR(result); // propagate error // switch (*result.err()) { ... } // handle error // // auto result = function(); // RETURN_IF_ERR(result); // propagate error // Helper to propagate errors. #define RETURN_IF_ERR(...) \ do { \ auto&& __result = __VA_ARGS__; \ if (__result.err()) { \ return ::android::base::Err(__result.unwrapErr()); \ } \ } while (false) namespace android { namespace base { namespace detail { template struct Ok { Ok(const T& value) : value(value) {} Ok(T&& value) : value(std::move(value)) {} Ok(Ok&& other) : value(std::move(other.value)) {} T value; }; template <> struct Ok { Ok() = default; }; template struct Err { public: Err(const E& value) : value(value) {} Err(E&& value) : value(std::move(value)) {} Err(Err&& other) : value(std::move(other.value)) {} E value; }; template struct ResultStorage { template ::value>::type> ResultStorage(Ok&& ok) : ok(std::move(ok.value)) {} ResultStorage(Err&& err) : err(std::move(err.value)) {} bool isOk() const { return ok.hasValue(); } Optional ok; Optional err; }; template struct ResultStorage { ResultStorage(Ok&& ok) {} ResultStorage(Err&& err) : err(std::move(err.value)) {} bool isOk() const { return !err.hasValue(); } Optional err; }; } // namespace detail template ::type> detail::Ok Ok(T&& value) { return detail::Ok(std::forward(value)); } static inline detail::Ok Ok() { return detail::Ok(); } template ::type> detail::Err Err(E&& value) { return detail::Err(std::forward(value)); } template class Result { public: template ::value>::type> Result(detail::Ok&& ok) : mStorage(std::move(ok)) {} Result(detail::Err&& err) : mStorage(std::move(err)) {} Result(Result&& other) : mStorage(std::move(other.mStorage)), mValid(other.mValid) { other.mValid = false; } // Returns an Optional representing the value, not defined is T is void. template typename std::enable_if::value && std::is_copy_constructible::value, Optional>::type ok() { CHECK(mValid) << "Result invalid"; return mStorage.ok; } template typename std::enable_if::value, const Optional&>::type ok() const { CHECK(mValid) << "Result invalid"; return mStorage.ok; } // For Result types, returns true if the Result is ok. template typename std::enable_if::value, bool>::type ok() const { CHECK(mValid) << "Result invalid"; return mStorage.isOk(); } // Returns an Optional representing the error, if it exists. template typename std::enable_if::value, Optional>::type err() { CHECK(mValid) << "Result invalid"; return mStorage.err; } const Optional& err() const { CHECK(mValid) << "Result invalid"; return mStorage.err; } // Unwraps the value and returns it. After this call the Result is invalid. template typename std::enable_if::value, U>::type unwrap() { CHECK(mValid) << "Result invalid"; mValid = false; return std::move(*(mStorage.ok.ptr())); } // Unwraps the error and returns it. After this call the Result is invalid. E unwrapErr() { CHECK(mValid) << "Result invalid"; mValid = false; return std::move(*(mStorage.err.ptr())); } private: detail::ResultStorage mStorage; bool mValid = true; }; } // namespace base } // namespace android