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<T,
StatusT> where StatusT is a wrapper type for status_t. This allows
OR_RETURN macro to be used in newer functions that returns Result<T,
StatusT>.
Example usage:
\#include <utils/ErrorsMacros.h>
status_t legacy_inner();
status_t legacy_outer() {
OR_RETURN(legacy_inner());
return OK;
}
Result<T, StatusT> new_outer() {
OR_RETURN(legacy_inner()); // the same macro
return T{...};
}
Bug: 209929099
Test: atest libutils_test
Change-Id: I0def0e84ce3f0c4ff6d508c202bd51902dfc9618
This commit is contained in:
parent
fcf3fefe0b
commit
705abe2d30
4 changed files with 203 additions and 2 deletions
|
|
@ -117,7 +117,7 @@ class ErrorIgnoreEnoent {
|
||||||
android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
|
android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
operator android::base::expected<T, ResultError<int>>() {
|
operator android::base::expected<T, ResultError<android::base::Errno>>() {
|
||||||
if (ignore_error_) {
|
if (ignore_error_) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,14 @@ cc_library_headers {
|
||||||
"libsystem_headers",
|
"libsystem_headers",
|
||||||
"libcutils_headers",
|
"libcutils_headers",
|
||||||
"libprocessgroup_headers",
|
"libprocessgroup_headers",
|
||||||
|
"libbase_headers",
|
||||||
],
|
],
|
||||||
export_header_lib_headers: [
|
export_header_lib_headers: [
|
||||||
"liblog_headers",
|
"liblog_headers",
|
||||||
"libsystem_headers",
|
"libsystem_headers",
|
||||||
"libcutils_headers",
|
"libcutils_headers",
|
||||||
"libprocessgroup_headers",
|
"libprocessgroup_headers",
|
||||||
|
"libbase_headers",
|
||||||
],
|
],
|
||||||
export_include_dirs: ["include"],
|
export_include_dirs: ["include"],
|
||||||
|
|
||||||
|
|
@ -297,13 +299,14 @@ cc_test {
|
||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"BitSet_test.cpp",
|
"BitSet_test.cpp",
|
||||||
|
"Errors_test.cpp",
|
||||||
"FileMap_test.cpp",
|
"FileMap_test.cpp",
|
||||||
"LruCache_test.cpp",
|
"LruCache_test.cpp",
|
||||||
"Mutex_test.cpp",
|
"Mutex_test.cpp",
|
||||||
"SharedBuffer_test.cpp",
|
"SharedBuffer_test.cpp",
|
||||||
"Singleton_test.cpp",
|
"Singleton_test.cpp",
|
||||||
"String8_test.cpp",
|
|
||||||
"String16_test.cpp",
|
"String16_test.cpp",
|
||||||
|
"String8_test.cpp",
|
||||||
"StrongPointer_test.cpp",
|
"StrongPointer_test.cpp",
|
||||||
"Timers_test.cpp",
|
"Timers_test.cpp",
|
||||||
"Unicode_test.cpp",
|
"Unicode_test.cpp",
|
||||||
|
|
@ -362,6 +365,7 @@ cc_test_library {
|
||||||
"-Wall",
|
"-Wall",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
],
|
],
|
||||||
|
header_libs: ["libutils_headers"],
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_test_library {
|
cc_test_library {
|
||||||
|
|
@ -374,6 +378,7 @@ cc_test_library {
|
||||||
"-Werror",
|
"-Werror",
|
||||||
],
|
],
|
||||||
shared_libs: ["libutils_test_singleton1"],
|
shared_libs: ["libutils_test_singleton1"],
|
||||||
|
header_libs: ["libutils_headers"],
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_benchmark {
|
cc_benchmark {
|
||||||
|
|
|
||||||
110
libutils/Errors_test.cpp
Normal file
110
libutils/Errors_test.cpp
Normal file
|
|
@ -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 <android-base/result.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
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<std::string, StatusT> {
|
||||||
|
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<int, StatusT> {
|
||||||
|
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<std::string, StatusT> {
|
||||||
|
if (success)
|
||||||
|
return "OK";
|
||||||
|
else
|
||||||
|
return Error<StatusT>(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);
|
||||||
|
}
|
||||||
86
libutils/include/utils/ErrorsMacros.h
Normal file
86
libutils/include/utils/ErrorsMacros.h
Normal file
|
|
@ -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 <android-base/errors.h>
|
||||||
|
#include <android-base/result.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
// StatusT is a wrapper class for status_t. Use this type instead of status_t when instantiating
|
||||||
|
// Result<T, E> and Error<E> 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<V> 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<status_t> {
|
||||||
|
// 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<status_t> Fail(status_t&& s) {
|
||||||
|
assert(!IsOk(s));
|
||||||
|
return OkOrFail<status_t>{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<T, StatusT>. This is used when OR_RETURN is used in a function whose
|
||||||
|
// return type is Result<T, StatusT>.
|
||||||
|
template <typename T, typename = std::enable_if_t<!std::is_same_v<T, status_t>>>
|
||||||
|
operator Result<T, StatusT>() && {
|
||||||
|
return Error<StatusT>(std::move(val_));
|
||||||
|
}
|
||||||
|
|
||||||
|
operator Result<int, StatusT>() && { return Error<StatusT>(std::move(val_)); }
|
||||||
|
|
||||||
|
// String representation of the error value.
|
||||||
|
static std::string ErrorMessage(const status_t& s) { return statusToString(s); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
} // namespace android
|
||||||
Loading…
Add table
Reference in a new issue