diff --git a/base/Android.bp b/base/Android.bp index 25a9f6884..7a2e66502 100644 --- a/base/Android.bp +++ b/base/Android.bp @@ -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 diff --git a/base/include/android-base/result.h b/base/include/android-base/result.h index 4a8e1ef21..f0e449ab4 100644 --- a/base/include/android-base/result.h +++ b/base/include/android-base/result.h @@ -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. In this case, -// the string that the ResultError takes is passed through the stream normally, but the errno is -// passed to the Result. 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. +// In this case, the string that the ResultError takes is passed through the stream normally, but +// the errno is passed to the Result. This can be used to pass errno from a failing C function up +// multiple callers. Note that when the outer Result 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. This is particularly useful if you have a // function that return Result but you have a Result and want to return its error. In this @@ -55,10 +60,10 @@ // Result 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 #include +#include + #include "android-base/expected.h" namespace android { @@ -147,16 +154,51 @@ class Error { Error& operator=(const Error&) = delete; Error& operator=(Error&&) = delete; + template + friend Error Errorf(const char* fmt, const Args&... args); + + template + 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 +inline int ErrorCode(int code, T&& t, const Args&... args) { + if constexpr (std::is_same_v>, ResultError>) { + return ErrorCode(t.code(), args...); + } + return ErrorCode(code, args...); +} + +template +inline Error Errorf(const char* fmt, const Args&... args) { + return Error(false, ErrorCode(0, args...), fmt::format(fmt, args...)); +} + +template +inline Error ErrnoErrorf(const char* fmt, const Args&... args) { + return Error(true, errno, fmt::format(fmt, args...)); +} + template using Result = android::base::expected; diff --git a/base/result_test.cpp b/base/result_test.cpp index e864b9708..2ee4057eb 100644 --- a/base/result_test.cpp +++ b/base/result_test.cpp @@ -355,5 +355,68 @@ TEST(result, preserve_errno) { EXPECT_EQ(old_errno, result2.error().code()); } +TEST(result, error_with_fmt) { + Result 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 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 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 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 inner1 = ErrnoErrorf("error1"); + + constexpr int errno2 = 10; + errno = errno2; + Result inner2 = ErrnoErrorf("error2"); + + // takes the error code of inner2 since its the last one. + Result 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