summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Zie Weaver <zearen@google.com>2023-12-13 07:59:42 -0800
committerGravatar Copybara-Service <copybara-worker@google.com>2023-12-13 08:00:40 -0800
commit031d99ab8761084ea3499688dc82c27abd10314e (patch)
tree3747a7e1a5780ea9597392514b772879ae950261
parentf16e457bce26a957c98f6e861de9a5c2092c8680 (diff)
Add `ostream<<` and `AbslStringify` to `absl::StatusOr`.
These methods will only be defined if they're defined for `T`. Additionally, we add jitter to the output to discourage people relying on the output format. PiperOrigin-RevId: 590598988 Change-Id: I4e7173b5f0c66fd3a1cdd3392944e20b8a26641f
-rw-r--r--absl/status/BUILD.bazel2
-rw-r--r--absl/status/CMakeLists.txt3
-rw-r--r--absl/status/internal/statusor_internal.h49
-rw-r--r--absl/status/statusor.h39
-rw-r--r--absl/status/statusor_test.cc37
5 files changed, 130 insertions, 0 deletions
diff --git a/absl/status/BUILD.bazel b/absl/status/BUILD.bazel
index a5fad4d4..981b37fd 100644
--- a/absl/status/BUILD.bazel
+++ b/absl/status/BUILD.bazel
@@ -105,6 +105,8 @@ cc_library(
"//absl/base:raw_logging_internal",
"//absl/meta:type_traits",
"//absl/strings",
+ "//absl/strings:has_ostream_operator",
+ "//absl/strings:str_format",
"//absl/types:variant",
"//absl/utility",
],
diff --git a/absl/status/CMakeLists.txt b/absl/status/CMakeLists.txt
index d9d16830..00415ab9 100644
--- a/absl/status/CMakeLists.txt
+++ b/absl/status/CMakeLists.txt
@@ -77,9 +77,11 @@ absl_cc_library(
absl::base
absl::config
absl::core_headers
+ absl::has_ostream_operator
absl::nullability
absl::raw_logging_internal
absl::status
+ absl::str_format
absl::strings
absl::type_traits
absl::utility
@@ -97,5 +99,6 @@ absl_cc_test(
DEPS
absl::status
absl::statusor
+ absl::strings
GTest::gmock_main
)
diff --git a/absl/status/internal/statusor_internal.h b/absl/status/internal/statusor_internal.h
index 25c11474..5be94903 100644
--- a/absl/status/internal/statusor_internal.h
+++ b/absl/status/internal/statusor_internal.h
@@ -14,6 +14,7 @@
#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
+#include <cstdint>
#include <type_traits>
#include <utility>
@@ -21,6 +22,7 @@
#include "absl/base/nullability.h"
#include "absl/meta/type_traits.h"
#include "absl/status/status.h"
+#include "absl/strings/string_view.h"
#include "absl/utility/utility.h"
namespace absl {
@@ -379,6 +381,53 @@ struct MoveAssignBase<T, false> {
ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status);
+// Used to introduce jitter into the output of printing functions for
+// `StatusOr` (i.e. `AbslStringify` and `operator<<`).
+class StringifyRandom {
+ enum BracesType {
+ kBareParens = 0,
+ kSpaceParens,
+ kBareBrackets,
+ kSpaceBrackets,
+ };
+
+ // Returns a random `BracesType` determined once per binary load.
+ static BracesType RandomBraces() {
+ static const BracesType kRandomBraces = static_cast<BracesType>(
+ (reinterpret_cast<uintptr_t>(&kRandomBraces) >> 4) % 4);
+ return kRandomBraces;
+ }
+
+ public:
+ static inline absl::string_view OpenBrackets() {
+ switch (RandomBraces()) {
+ case kBareParens:
+ return "(";
+ case kSpaceParens:
+ return "( ";
+ case kBareBrackets:
+ return "[";
+ case kSpaceBrackets:
+ return "[ ";
+ }
+ return "(";
+ }
+
+ static inline absl::string_view CloseBrackets() {
+ switch (RandomBraces()) {
+ case kBareParens:
+ return ")";
+ case kSpaceParens:
+ return " )";
+ case kBareBrackets:
+ return "]";
+ case kSpaceBrackets:
+ return " ]";
+ }
+ return ")";
+ }
+};
+
} // namespace internal_statusor
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/status/statusor.h b/absl/status/statusor.h
index 33a2cf31..cd35e5b4 100644
--- a/absl/status/statusor.h
+++ b/absl/status/statusor.h
@@ -39,6 +39,7 @@
#include <exception>
#include <initializer_list>
#include <new>
+#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
@@ -49,6 +50,9 @@
#include "absl/meta/type_traits.h"
#include "absl/status/internal/statusor_internal.h"
#include "absl/status/status.h"
+#include "absl/strings/has_absl_stringify.h"
+#include "absl/strings/has_ostream_operator.h"
+#include "absl/strings/str_format.h"
#include "absl/types/variant.h"
#include "absl/utility/utility.h"
@@ -651,6 +655,41 @@ bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
return !(lhs == rhs);
}
+// Prints the `value` or the status in brackets to `os`.
+//
+// Requires `T` supports `operator<<`. Do not rely on the output format which
+// may change without notice.
+template <typename T, typename std::enable_if<
+ absl::HasOstreamOperator<T>::value, int>::type = 0>
+std::ostream& operator<<(std::ostream& os, const StatusOr<T>& status_or) {
+ if (status_or.ok()) {
+ os << status_or.value();
+ } else {
+ os << internal_statusor::StringifyRandom::OpenBrackets()
+ << status_or.status()
+ << internal_statusor::StringifyRandom::CloseBrackets();
+ }
+ return os;
+}
+
+// As above, but supports `StrCat`, `StrFormat`, etc.
+//
+// Requires `T` has `AbslStringify`. Do not rely on the output format which
+// may change without notice.
+template <
+ typename Sink, typename T,
+ typename std::enable_if<absl::HasAbslStringify<T>::value, int>::type = 0>
+void AbslStringify(Sink& sink, const StatusOr<T>& status_or) {
+ if (status_or.ok()) {
+ absl::Format(&sink, "%v", status_or.value());
+ } else {
+ absl::Format(&sink, "%s%v%s",
+ internal_statusor::StringifyRandom::OpenBrackets(),
+ status_or.status(),
+ internal_statusor::StringifyRandom::CloseBrackets());
+ }
+}
+
//------------------------------------------------------------------------------
// Implementation details for StatusOr<T>
//------------------------------------------------------------------------------
diff --git a/absl/status/statusor_test.cc b/absl/status/statusor_test.cc
index b4967e46..09ffc658 100644
--- a/absl/status/statusor_test.cc
+++ b/absl/status/statusor_test.cc
@@ -19,6 +19,8 @@
#include <initializer_list>
#include <map>
#include <memory>
+#include <ostream>
+#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
@@ -29,6 +31,7 @@
#include "absl/base/casts.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
+#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/any.h"
#include "absl/types/variant.h"
@@ -37,13 +40,16 @@
namespace {
using ::testing::AllOf;
+using ::testing::AnyOf;
using ::testing::AnyWith;
using ::testing::ElementsAre;
+using ::testing::EndsWith;
using ::testing::Field;
using ::testing::HasSubstr;
using ::testing::Ne;
using ::testing::Not;
using ::testing::Pointee;
+using ::testing::StartsWith;
using ::testing::VariantWith;
#ifdef GTEST_HAS_STATUS_MATCHERS
@@ -1881,4 +1887,35 @@ TEST(StatusOr, StatusAssignmentFromTypeConvertibleToStatus) {
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v));
}
+struct PrintTestStruct {
+ friend std::ostream& operator<<(std::ostream& os, const PrintTestStruct&) {
+ return os << "ostream";
+ }
+
+ template <typename Sink>
+ friend void AbslStringify(Sink& sink, const PrintTestStruct&) {
+ sink.Append("stringify");
+ }
+};
+
+TEST(StatusOr, OkPrinting) {
+ absl::StatusOr<PrintTestStruct> print_me = PrintTestStruct{};
+ std::stringstream stream;
+ stream << print_me;
+ EXPECT_EQ(stream.str(), "ostream");
+ EXPECT_EQ(absl::StrCat(print_me), "stringify");
+}
+
+TEST(StatusOr, ErrorPrinting) {
+ absl::StatusOr<PrintTestStruct> print_me = absl::UnknownError("error");
+ std::stringstream stream;
+ stream << print_me;
+ const auto error_matcher =
+ AllOf(HasSubstr("UNKNOWN"), HasSubstr("error"),
+ AnyOf(AllOf(StartsWith("("), EndsWith(")")),
+ AllOf(StartsWith("["), EndsWith("]"))));
+ EXPECT_THAT(stream.str(), error_matcher);
+ EXPECT_THAT(absl::StrCat(print_me), error_matcher);
+}
+
} // namespace