From 705abe2d309be549e4080cb4f3fccc92f4b8016c Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Tue, 14 Dec 2021 22:55:34 +0900 Subject: [PATCH] OR_RETURN supports status_t This change provide a specialization of android::base::OkOrFail for status_t. As a result, a statement whose type is status_t can be used with OR_RETURN. The specialization also provides conversion operators to Result where StatusT is a wrapper type for status_t. This allows OR_RETURN macro to be used in newer functions that returns Result. Example usage: \#include status_t legacy_inner(); status_t legacy_outer() { OR_RETURN(legacy_inner()); return OK; } Result new_outer() { OR_RETURN(legacy_inner()); // the same macro return T{...}; } Bug: 209929099 Test: atest libutils_test Change-Id: I0def0e84ce3f0c4ff6d508c202bd51902dfc9618 --- init/builtins.cpp | 2 +- libutils/Android.bp | 7 +- libutils/Errors_test.cpp | 110 ++++++++++++++++++++++++++ libutils/include/utils/ErrorsMacros.h | 86 ++++++++++++++++++++ 4 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 libutils/Errors_test.cpp create mode 100644 libutils/include/utils/ErrorsMacros.h diff --git a/init/builtins.cpp b/init/builtins.cpp index cc445be4c..0eb894bec 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -117,7 +117,7 @@ class ErrorIgnoreEnoent { android::base::GetMinimumLogSeverity() > android::base::DEBUG) {} template - operator android::base::expected>() { + operator android::base::expected>() { if (ignore_error_) { return {}; } diff --git a/libutils/Android.bp b/libutils/Android.bp index e6d9a4c6e..45d010f33 100644 --- a/libutils/Android.bp +++ b/libutils/Android.bp @@ -32,12 +32,14 @@ cc_library_headers { "libsystem_headers", "libcutils_headers", "libprocessgroup_headers", + "libbase_headers", ], export_header_lib_headers: [ "liblog_headers", "libsystem_headers", "libcutils_headers", "libprocessgroup_headers", + "libbase_headers", ], export_include_dirs: ["include"], @@ -297,13 +299,14 @@ cc_test { srcs: [ "BitSet_test.cpp", + "Errors_test.cpp", "FileMap_test.cpp", "LruCache_test.cpp", "Mutex_test.cpp", "SharedBuffer_test.cpp", "Singleton_test.cpp", - "String8_test.cpp", "String16_test.cpp", + "String8_test.cpp", "StrongPointer_test.cpp", "Timers_test.cpp", "Unicode_test.cpp", @@ -362,6 +365,7 @@ cc_test_library { "-Wall", "-Werror", ], + header_libs: ["libutils_headers"], } cc_test_library { @@ -374,6 +378,7 @@ cc_test_library { "-Werror", ], shared_libs: ["libutils_test_singleton1"], + header_libs: ["libutils_headers"], } cc_benchmark { diff --git a/libutils/Errors_test.cpp b/libutils/Errors_test.cpp new file mode 100644 index 000000000..873c99490 --- /dev/null +++ b/libutils/Errors_test.cpp @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#include "utils/ErrorsMacros.h" + +#include + +#include + +using namespace android; + +using android::base::Error; +using android::base::Result; + +status_t success_or_fail(bool success) { + if (success) + return OK; + else + return PERMISSION_DENIED; +} + +TEST(errors, unwrap_or_return) { + auto f = [](bool success, int* val) -> status_t { + OR_RETURN(success_or_fail(success)); + *val = 10; + return OK; + }; + + int val; + status_t s = f(true, &val); + EXPECT_EQ(OK, s); + EXPECT_EQ(10, val); + + val = 0; // reset + status_t q = f(false, &val); + EXPECT_EQ(PERMISSION_DENIED, q); + EXPECT_EQ(0, val); +} + +TEST(errors, unwrap_or_return_result) { + auto f = [](bool success) -> Result { + OR_RETURN(success_or_fail(success)); + return "hello"; + }; + + auto r = f(true); + EXPECT_TRUE(r.ok()); + EXPECT_EQ("hello", *r); + + auto s = f(false); + EXPECT_FALSE(s.ok()); + EXPECT_EQ(PERMISSION_DENIED, s.error().code()); + EXPECT_EQ("PERMISSION_DENIED", s.error().message()); +} + +TEST(errors, unwrap_or_return_result_int) { + auto f = [](bool success) -> Result { + OR_RETURN(success_or_fail(success)); + return 10; + }; + + auto r = f(true); + EXPECT_TRUE(r.ok()); + EXPECT_EQ(10, *r); + + auto s = f(false); + EXPECT_FALSE(s.ok()); + EXPECT_EQ(PERMISSION_DENIED, s.error().code()); + EXPECT_EQ("PERMISSION_DENIED", s.error().message()); +} + +TEST(errors, unwrap_or_fatal) { + OR_FATAL(success_or_fail(true)); + + EXPECT_DEATH(OR_FATAL(success_or_fail(false)), "PERMISSION_DENIED"); +} + +TEST(errors, result_in_status) { + auto f = [](bool success) -> Result { + if (success) + return "OK"; + else + return Error(PERMISSION_DENIED) << "custom error message"; + }; + + auto g = [&](bool success) -> status_t { + std::string val = OR_RETURN(f(success)); + EXPECT_EQ("OK", val); + return OK; + }; + + status_t a = g(true); + EXPECT_EQ(OK, a); + + status_t b = g(false); + EXPECT_EQ(PERMISSION_DENIED, b); +} diff --git a/libutils/include/utils/ErrorsMacros.h b/libutils/include/utils/ErrorsMacros.h new file mode 100644 index 000000000..048c5386f --- /dev/null +++ b/libutils/include/utils/ErrorsMacros.h @@ -0,0 +1,86 @@ +/* + * 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 + +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 { + +// 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 { + // 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 Error(std::move(val_)); + } + + operator Result() && { return Error(std::move(val_)); } + + // String representation of the error value. + static std::string ErrorMessage(const status_t& s) { return statusToString(s); } +}; + +} // namespace base +} // namespace android