diff --git a/webrtc/base/optional.h b/webrtc/base/optional.h index f5354ee0f2..c8ed069d55 100644 --- a/webrtc/base/optional.h +++ b/webrtc/base/optional.h @@ -15,6 +15,11 @@ #include #include +#ifdef UNIT_TEST +#include +#include +#endif // UNIT_TEST + #include "webrtc/base/array_view.h" #include "webrtc/base/checks.h" #include "webrtc/base/sanitizer.h" @@ -25,7 +30,9 @@ namespace optional_internal { #if RTC_HAS_ASAN -// This is a non-inlined function. The optimizer can't see inside it. +// This is a non-inlined function. The optimizer can't see inside it. It +// prevents the compiler from generating optimized code that reads value_ even +// if it is unset. Although safe, this causes memory sanitizers to complain. void* FunctionThatDoesNothingImpl(void*); template @@ -296,6 +303,98 @@ class Optional final { }; }; +#ifdef UNIT_TEST +namespace optional_internal { + +// Checks if there's a valid PrintTo(const T&, std::ostream*) call for T. +template +struct HasPrintTo { + private: + struct No {}; + + template + static auto Test(const T2& obj) + -> decltype(PrintTo(obj, std::declval())); + + template + static No Test(...); + + public: + static constexpr bool value = + !std::is_same(std::declval())), No>::value; +}; + +// Checks if there's a valid operator<<(std::ostream&, const T&) call for T. +template +struct HasOstreamOperator { + private: + struct No {}; + + template + static auto Test(const T2& obj) + -> decltype(std::declval() << obj); + + template + static No Test(...); + + public: + static constexpr bool value = + !std::is_same(std::declval())), No>::value; +}; + +// Prefer using PrintTo to print the object. +template +typename std::enable_if::value, void>::type OptionalPrintToHelper( + const T& value, + std::ostream* os) { + PrintTo(value, os); +} + +// Fall back to operator<<(std::ostream&, ...) if it exists. +template +typename std::enable_if::value && !HasPrintTo::value, + void>::type +OptionalPrintToHelper(const T& value, std::ostream* os) { + *os << value; +} + +inline void OptionalPrintObjectBytes(const unsigned char* bytes, + size_t size, + std::ostream* os) { + *os << "(bytes[i]); + } + *os << "]>"; +} + +// As a final back-up, just print the contents of the objcets byte-wise. +template +typename std::enable_if::value && !HasPrintTo::value, + void>::type +OptionalPrintToHelper(const T& value, std::ostream* os) { + OptionalPrintObjectBytes(reinterpret_cast(&value), + sizeof(value), os); +} + +} // namespace optional_internal + +// PrintTo is used by gtest to print out the results of tests. We want to ensure +// the object contained in an Optional can be printed out if it's set, while +// avoiding touching the object's storage if it is undefined. +template +void PrintTo(const rtc::Optional& opt, std::ostream* os) { + if (opt) { + optional_internal::OptionalPrintToHelper(*opt, os); + } else { + *os << ""; + } +} + +#endif // UNIT_TEST + } // namespace rtc #endif // WEBRTC_BASE_OPTIONAL_H_ diff --git a/webrtc/base/optional_unittest.cc b/webrtc/base/optional_unittest.cc index 65070fabb0..cc9d2f9164 100644 --- a/webrtc/base/optional_unittest.cc +++ b/webrtc/base/optional_unittest.cc @@ -21,6 +21,34 @@ namespace rtc { namespace { +struct MyUnprintableType { + int value; +}; + +struct MyPrintableType { + int value; +}; + +struct MyOstreamPrintableType { + int value; +}; + +void PrintTo(const MyPrintableType& mpt, std::ostream* os) { + *os << "The value is " << mpt.value; +} + +std::ostream& operator<<(std::ostream& os, + const MyPrintableType& mpt) { + os << mpt.value; + return os; +} + +std::ostream& operator<<(std::ostream& os, + const MyOstreamPrintableType& mpt) { + os << mpt.value; + return os; +} + // Class whose instances logs various method calls (constructor, destructor, // etc.). Each instance has a unique ID (a simple global sequence number) and // an origin ID. When a copy is made, the new object gets a fresh ID but copies @@ -740,4 +768,32 @@ TEST(OptionalTest, TestMoveValue) { *log); } +TEST(OptionalTest, TestPrintTo) { + constexpr char kEmptyOptionalMessage[] = ""; + const Optional empty_unprintable; + const Optional empty_printable; + const Optional empty_ostream_printable; + EXPECT_EQ(kEmptyOptionalMessage, ::testing::PrintToString(empty_unprintable)); + EXPECT_EQ(kEmptyOptionalMessage, ::testing::PrintToString(empty_printable)); + EXPECT_EQ(kEmptyOptionalMessage, + ::testing::PrintToString(empty_ostream_printable)); + EXPECT_NE("1", ::testing::PrintToString(Optional({1}))); + EXPECT_NE("1", ::testing::PrintToString(Optional({1}))); + EXPECT_EQ("The value is 1", + ::testing::PrintToString(Optional({1}))); + EXPECT_EQ("1", + ::testing::PrintToString(Optional({1}))); +} + +void UnusedFunctionWorkaround() { + // These are here to ensure we don't get warnings about ostream and PrintTo + // for MyPrintableType never getting called. + const MyPrintableType dont_warn{17}; + const MyOstreamPrintableType dont_warn2{18}; + std::stringstream sstr; + sstr << dont_warn; + PrintTo(dont_warn, &sstr); + sstr << dont_warn2; +} + } // namespace rtc