Merge "Add Errorf and ErrnoErrorf" am: ebbe9e7391
am: a7c8b3c49f
Change-Id: Ie8b87bd13a324b9cc97b69a7a179e3e056919498
This commit is contained in:
commit
b208032521
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue