/* * Copyright (C) 2021 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 "Errors.h" // It would have been better if this file (ErrorsMacros.h) is entirely in utils/Errors.h. However // that is infeasible as some (actually many) are using utils/Errors.h via the implicit include path // `system/core/include` [1]. Since such users are not guaranteed to specify the dependency to // libbase_headers, the following headers from libbase_headers can't be found. // [1] build/soong/cc/config/global.go#commonGlobalIncludes #include #include #include #include namespace android { // StatusT is a wrapper class for status_t. Use this type instead of status_t when instantiating // Result and Error template classes. This is required to distinguish status_t from // other integer-based error code types like errno, and also to provide utility functions like // print(). struct StatusT { StatusT() : val_(OK) {} StatusT(status_t s) : val_(s) {} const status_t& value() const { return val_; } operator status_t() const { return val_; } std::string print() const { return statusToString(val_); } status_t val_; }; namespace base { // TODO(b/221235365) StatusT fulfill ResultError contract and cleanup. // Unlike typical ResultError types, the underlying code should be a status_t // instead of a StatusT. We also special-case message generation. template<> struct ResultError { ResultError(status_t s) : val_(s) { LOG_FATAL_IF(s == OK, "Result error should not hold success"); } template operator expected>() const { return unexpected(*this); } std::string message() const { return statusToString(val_); } status_t code() const { return val_; } private: const status_t val_; }; template<> struct ResultError { template ResultError(T&& message, status_t s) : val_(s), message_(std::forward(message)) { LOG_FATAL_IF(s == OK, "Result error should not hold success"); } ResultError(status_t s) : val_(s) {} template operator expected>() const { return unexpected(*this); } status_t code() const { return val_; } std::string message() const { return statusToString(val_) + message_; } private: const status_t val_; std::string message_; }; // Specialization of android::base::OkOrFail for V = status_t. This is used to use the OR_RETURN // and OR_FATAL macros with statements that yields a value of status_t. See android-base/errors.h // for the detailed contract. template <> struct OkOrFail { static_assert(std::is_same_v); // Tests if status_t is a success value of not. static bool IsOk(const status_t& s) { return s == OK; } // Unwrapping status_t in the success case is just asserting that it is actually a success. // We don't return OK because it would be redundant. static void Unwrap([[maybe_unused]] status_t&& s) { assert(IsOk(s)); } // Consumes status_t when it's a fail value static OkOrFail Fail(status_t&& s) { assert(!IsOk(s)); return OkOrFail{s}; } status_t val_; // And converts back into status_t. This is used when OR_RETURN is used in a function whose // return type is status_t. operator status_t() && { return val_; } // Or converts into Result. This is used when OR_RETURN is used in a function whose // return type is Result. template operator Result() && { return ResultError(std::move(val_)); } template operator Result() && { return ResultError(std::move(val_)); } // Since user defined conversion can be followed by numeric conversion, // we have to specialize all conversions to results holding numeric types to // avoid conversion ambiguities with the constructor of expected. #pragma push_macro("SPECIALIZED_CONVERSION") #define SPECIALIZED_CONVERSION(type)\ operator Result() && { return ResultError(std::move(val_)); }\ operator Result() && { return ResultError(std::move(val_));} SPECIALIZED_CONVERSION(int) SPECIALIZED_CONVERSION(short int) SPECIALIZED_CONVERSION(unsigned short int) SPECIALIZED_CONVERSION(unsigned int) SPECIALIZED_CONVERSION(long int) SPECIALIZED_CONVERSION(unsigned long int) SPECIALIZED_CONVERSION(long long int) SPECIALIZED_CONVERSION(unsigned long long int) SPECIALIZED_CONVERSION(bool) SPECIALIZED_CONVERSION(char) SPECIALIZED_CONVERSION(unsigned char) SPECIALIZED_CONVERSION(signed char) SPECIALIZED_CONVERSION(wchar_t) SPECIALIZED_CONVERSION(char16_t) SPECIALIZED_CONVERSION(char32_t) SPECIALIZED_CONVERSION(float) SPECIALIZED_CONVERSION(double) SPECIALIZED_CONVERSION(long double) #undef SPECIALIZED_CONVERSION #pragma pop_macro("SPECIALIZED_CONVERSION") // String representation of the error value. static std::string ErrorMessage(const status_t& s) { return statusToString(s); } }; } // namespace base // These conversions make StatusT directly comparable to status_t in order to // avoid calling code whenever comparisons are desired. template bool operator==(const base::ResultError& l, const status_t& r) { return (l.code() == r); } template bool operator==(const status_t& l, const base::ResultError& r) { return (l == r.code()); } template bool operator!=(const base::ResultError& l, const status_t& r) { return (l.code() != r); } template bool operator!=(const status_t& l, const base::ResultError& r) { return (l != r.code()); } } // namespace android