Commit 031d99ab by Zie Weaver Committed by Copybara-Service

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
parent f16e457b
...@@ -105,6 +105,8 @@ cc_library( ...@@ -105,6 +105,8 @@ cc_library(
"//absl/base:raw_logging_internal", "//absl/base:raw_logging_internal",
"//absl/meta:type_traits", "//absl/meta:type_traits",
"//absl/strings", "//absl/strings",
"//absl/strings:has_ostream_operator",
"//absl/strings:str_format",
"//absl/types:variant", "//absl/types:variant",
"//absl/utility", "//absl/utility",
], ],
......
...@@ -77,9 +77,11 @@ absl_cc_library( ...@@ -77,9 +77,11 @@ absl_cc_library(
absl::base absl::base
absl::config absl::config
absl::core_headers absl::core_headers
absl::has_ostream_operator
absl::nullability absl::nullability
absl::raw_logging_internal absl::raw_logging_internal
absl::status absl::status
absl::str_format
absl::strings absl::strings
absl::type_traits absl::type_traits
absl::utility absl::utility
...@@ -97,5 +99,6 @@ absl_cc_test( ...@@ -97,5 +99,6 @@ absl_cc_test(
DEPS DEPS
absl::status absl::status
absl::statusor absl::statusor
absl::strings
GTest::gmock_main GTest::gmock_main
) )
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ #ifndef ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
#define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_ #define ABSL_STATUS_INTERNAL_STATUSOR_INTERNAL_H_
#include <cstdint>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
...@@ -21,6 +22,7 @@ ...@@ -21,6 +22,7 @@
#include "absl/base/nullability.h" #include "absl/base/nullability.h"
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/status/status.h" #include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/utility/utility.h" #include "absl/utility/utility.h"
namespace absl { namespace absl {
...@@ -379,6 +381,53 @@ struct MoveAssignBase<T, false> { ...@@ -379,6 +381,53 @@ struct MoveAssignBase<T, false> {
ABSL_ATTRIBUTE_NORETURN void ThrowBadStatusOrAccess(absl::Status status); 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 } // namespace internal_statusor
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <exception> #include <exception>
#include <initializer_list> #include <initializer_list>
#include <new> #include <new>
#include <ostream>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
...@@ -49,6 +50,9 @@ ...@@ -49,6 +50,9 @@
#include "absl/meta/type_traits.h" #include "absl/meta/type_traits.h"
#include "absl/status/internal/statusor_internal.h" #include "absl/status/internal/statusor_internal.h"
#include "absl/status/status.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/types/variant.h"
#include "absl/utility/utility.h" #include "absl/utility/utility.h"
...@@ -651,6 +655,41 @@ bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) { ...@@ -651,6 +655,41 @@ bool operator!=(const StatusOr<T>& lhs, const StatusOr<T>& rhs) {
return !(lhs == 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> // Implementation details for StatusOr<T>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <initializer_list> #include <initializer_list>
#include <map> #include <map>
#include <memory> #include <memory>
#include <ostream>
#include <sstream>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
...@@ -29,6 +31,7 @@ ...@@ -29,6 +31,7 @@
#include "absl/base/casts.h" #include "absl/base/casts.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
#include "absl/status/status.h" #include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/types/any.h" #include "absl/types/any.h"
#include "absl/types/variant.h" #include "absl/types/variant.h"
...@@ -37,13 +40,16 @@ ...@@ -37,13 +40,16 @@
namespace { namespace {
using ::testing::AllOf; using ::testing::AllOf;
using ::testing::AnyOf;
using ::testing::AnyWith; using ::testing::AnyWith;
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::EndsWith;
using ::testing::Field; using ::testing::Field;
using ::testing::HasSubstr; using ::testing::HasSubstr;
using ::testing::Ne; using ::testing::Ne;
using ::testing::Not; using ::testing::Not;
using ::testing::Pointee; using ::testing::Pointee;
using ::testing::StartsWith;
using ::testing::VariantWith; using ::testing::VariantWith;
#ifdef GTEST_HAS_STATUS_MATCHERS #ifdef GTEST_HAS_STATUS_MATCHERS
...@@ -1881,4 +1887,35 @@ TEST(StatusOr, StatusAssignmentFromTypeConvertibleToStatus) { ...@@ -1881,4 +1887,35 @@ TEST(StatusOr, StatusAssignmentFromTypeConvertibleToStatus) {
EXPECT_EQ(statusor.status(), static_cast<absl::Status>(v)); 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 } // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment