Add Errorf and ErrnoErrorf
String formatting for error message can be done by using Errorf and
ErrnoErrorf. e.g. `return Errorf("{} failures", num_failure);`
The string format follows that of fmtlib, not printf.
Bug: 132145659
Test: libbase_test
Change-Id: I65f45d5f12ed23e2c14d6eb442278f467cc42e83
diff --git a/base/Android.bp b/base/Android.bp
index 25a9f68..7a2e665 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -111,6 +111,9 @@
"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 @@
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 4a8e1ef..f0e449a 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<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 @@
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>;
diff --git a/base/result_test.cpp b/base/result_test.cpp
index e864b97..2ee4057 100644
--- a/base/result_test.cpp
+++ b/base/result_test.cpp
@@ -355,5 +355,68 @@
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