Loading base/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 Loading base/include/android-base/result.h +49 −7 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; // } Loading @@ -74,6 +79,8 @@ #include <sstream> #include <string> #include <fmt/ostream.h> #include "android-base/expected.h" namespace android { Loading Loading @@ -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>; Loading base/result_test.cpp +63 −0 Original line number Diff line number Diff line Loading @@ -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
base/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 Loading
base/include/android-base/result.h +49 −7 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; // } Loading @@ -74,6 +79,8 @@ #include <sstream> #include <string> #include <fmt/ostream.h> #include "android-base/expected.h" namespace android { Loading Loading @@ -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>; Loading
base/result_test.cpp +63 −0 Original line number Diff line number Diff line Loading @@ -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