Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ebbe9e73 authored by Tom Cherry's avatar Tom Cherry Committed by Gerrit Code Review
Browse files

Merge "Add Errorf and ErrnoErrorf"

parents 2d620c00 20524ed6
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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
+49 −7
Original line number Diff line number Diff line
@@ -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>;

+63 −0
Original line number Diff line number Diff line
@@ -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