Merge "Add Errorf and ErrnoErrorf" am: ebbe9e7391

am: a7c8b3c49f

Change-Id: Ie8b87bd13a324b9cc97b69a7a179e3e056919498
This commit is contained in:
Tom Cherry 2019-06-14 11:47:33 -07:00 committed by android-build-merger
commit b208032521
3 changed files with 118 additions and 7 deletions

View File

@ -111,6 +111,9 @@ cc_library {
"libbase_headers",
],
export_header_lib_headers: ["libbase_headers"],
static_libs: ["fmtlib"],
whole_static_libs: ["fmtlib"],
export_static_lib_headers: ["fmtlib"],
}
cc_library_static {
@ -119,6 +122,9 @@ cc_library_static {
sdk_version: "current",
stl: "c++_static",
export_include_dirs: ["include"],
static_libs: ["fmtlib_ndk"],
whole_static_libs: ["fmtlib_ndk"],
export_static_lib_headers: ["fmtlib_ndk"],
}
// Tests

View File

@ -42,10 +42,15 @@
// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
// value can be directly specified via the Error() constructor.
//
// ResultError can be used in the ostream when using Error to construct a Result<T>. In this case,
// the string that the ResultError takes is passed through the stream normally, but the errno is
// passed to the Result<T>. This can be used to pass errno from a failing C function up multiple
// callers.
// Errorf and ErrnoErrorf accept the format string syntax of the fmblib (https://fmt.dev).
// Errorf("{} errors", num) is equivalent to Error() << num << " errors".
//
// ResultError can be used in the ostream and when using Error/Errorf to construct a Result<T>.
// In this case, the string that the ResultError takes is passed through the stream normally, but
// the errno is passed to the Result<T>. This can be used to pass errno from a failing C function up
// multiple callers. Note that when the outer Result<T> is created with ErrnoError/ErrnoErrorf then
// the errno from the inner ResultError is not passed. Also when multiple ResultError objects are
// used, the errno of the last one is respected.
//
// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
// function that return Result<T> but you have a Result<U> and want to return its error. In this
@ -55,10 +60,10 @@
// Result<U> CalculateResult(const T& input) {
// U output;
// if (!SomeOtherCppFunction(input, &output)) {
// return Error() << "SomeOtherCppFunction(" << input << ") failed";
// return Errorf("SomeOtherCppFunction {} failed", input);
// }
// if (!c_api_function(output)) {
// return ErrnoError() << "c_api_function(" << output << ") failed";
// return ErrnoErrorf("c_api_function {} failed", output);
// }
// return output;
// }
@ -74,6 +79,8 @@
#include <sstream>
#include <string>
#include <fmt/ostream.h>
#include "android-base/expected.h"
namespace android {
@ -147,16 +154,51 @@ class Error {
Error& operator=(const Error&) = delete;
Error& operator=(Error&&) = delete;
template <typename... Args>
friend Error Errorf(const char* fmt, const Args&... args);
template <typename... Args>
friend Error ErrnoErrorf(const char* fmt, const Args&... args);
private:
Error(bool append_errno, int errno_to_append, const std::string& message)
: errno_(errno_to_append), append_errno_(append_errno) {
(*this) << message;
}
std::stringstream ss_;
int errno_;
bool append_errno_;
const bool append_errno_;
};
inline Error ErrnoError() {
return Error(errno);
}
inline int ErrorCode(int code) {
return code;
}
// Return the error code of the last ResultError object, if any.
// Otherwise, return `code` as it is.
template <typename T, typename... Args>
inline int ErrorCode(int code, T&& t, const Args&... args) {
if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<T>>, ResultError>) {
return ErrorCode(t.code(), args...);
}
return ErrorCode(code, args...);
}
template <typename... Args>
inline Error Errorf(const char* fmt, const Args&... args) {
return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...));
}
template <typename... Args>
inline Error ErrnoErrorf(const char* fmt, const Args&... args) {
return Error(true, errno, fmt::format(fmt, args...));
}
template <typename T>
using Result = android::base::expected<T, ResultError>;

View File

@ -355,5 +355,68 @@ TEST(result, preserve_errno) {
EXPECT_EQ(old_errno, result2.error().code());
}
TEST(result, error_with_fmt) {
Result<int> result = Errorf("{} {}!", "hello", "world");
EXPECT_EQ("hello world!", result.error().message());
result = Errorf("{} {}!", std::string("hello"), std::string("world"));
EXPECT_EQ("hello world!", result.error().message());
result = Errorf("{h} {w}!", fmt::arg("w", "world"), fmt::arg("h", "hello"));
EXPECT_EQ("hello world!", result.error().message());
result = Errorf("hello world!");
EXPECT_EQ("hello world!", result.error().message());
Result<int> result2 = Errorf("error occurred with {}", result.error());
EXPECT_EQ("error occurred with hello world!", result2.error().message());
constexpr int test_errno = 6;
errno = test_errno;
result = ErrnoErrorf("{} {}!", "hello", "world");
EXPECT_EQ(test_errno, result.error().code());
EXPECT_EQ("hello world!: "s + strerror(test_errno), result.error().message());
}
TEST(result, error_with_fmt_carries_errno) {
constexpr int inner_errno = 6;
errno = inner_errno;
Result<int> inner_result = ErrnoErrorf("inner failure");
errno = 0;
EXPECT_EQ(inner_errno, inner_result.error().code());
// outer_result is created with Errorf, but its error code is got from inner_result.
Result<int> outer_result = Errorf("outer failure caused by {}", inner_result.error());
EXPECT_EQ(inner_errno, outer_result.error().code());
EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno),
outer_result.error().message());
// now both result objects are created with ErrnoErrorf. errno from the inner_result
// is not passed to outer_result.
constexpr int outer_errno = 10;
errno = outer_errno;
outer_result = ErrnoErrorf("outer failure caused by {}", inner_result.error());
EXPECT_EQ(outer_errno, outer_result.error().code());
EXPECT_EQ("outer failure caused by inner failure: "s + strerror(inner_errno) + ": "s +
strerror(outer_errno),
outer_result.error().message());
}
TEST(result, errno_chaining_multiple) {
constexpr int errno1 = 6;
errno = errno1;
Result<int> inner1 = ErrnoErrorf("error1");
constexpr int errno2 = 10;
errno = errno2;
Result<int> inner2 = ErrnoErrorf("error2");
// takes the error code of inner2 since its the last one.
Result<int> outer = Errorf("two errors: {}, {}", inner1.error(), inner2.error());
EXPECT_EQ(errno2, outer.error().code());
EXPECT_EQ("two errors: error1: "s + strerror(errno1) + ", error2: "s + strerror(errno2),
outer.error().message());
}
} // namespace base
} // namespace android