diff options
author | Zie Weaver <zearen@google.com> | 2023-12-13 07:59:42 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-12-13 08:00:40 -0800 |
commit | 031d99ab8761084ea3499688dc82c27abd10314e (patch) | |
tree | 3747a7e1a5780ea9597392514b772879ae950261 /absl | |
parent | f16e457bce26a957c98f6e861de9a5c2092c8680 (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
Diffstat (limited to 'absl')
-rw-r--r-- | absl/status/BUILD.bazel | 2 | ||||
-rw-r--r-- | absl/status/CMakeLists.txt | 3 | ||||
-rw-r--r-- | absl/status/internal/statusor_internal.h | 49 | ||||
-rw-r--r-- | absl/status/statusor.h | 39 | ||||
-rw-r--r-- | absl/status/statusor_test.cc | 37 |
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 |