diff options
-rw-r--r-- | CMake/AbseilDll.cmake | 1 | ||||
-rw-r--r-- | absl/copts/GENERATED_AbseilCopts.cmake | 11 | ||||
-rw-r--r-- | absl/copts/GENERATED_copts.bzl | 11 | ||||
-rw-r--r-- | absl/copts/copts.py | 6 | ||||
-rw-r--r-- | absl/log/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/log/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/log/internal/log_message.h | 48 | ||||
-rw-r--r-- | absl/log/log.h | 37 | ||||
-rw-r--r-- | absl/log/log_format_test.cc | 131 | ||||
-rw-r--r-- | absl/strings/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/strings/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/strings/internal/has_absl_stringify.h | 55 | ||||
-rw-r--r-- | absl/strings/internal/stringify_sink.h | 9 | ||||
-rw-r--r-- | absl/strings/str_cat.h | 1 | ||||
-rw-r--r-- | absl/synchronization/lifetime_test.cc | 3 |
15 files changed, 275 insertions, 42 deletions
diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index b81b0cea..831ec5fb 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -244,6 +244,7 @@ set(ABSL_INTERNAL_DLL_FILES "strings/internal/string_constant.h" "strings/internal/stringify_sink.h" "strings/internal/stringify_sink.cc" + "strings/internal/has_absl_stringify.h" "strings/match.cc" "strings/match.h" "strings/numbers.cc" diff --git a/absl/copts/GENERATED_AbseilCopts.cmake b/absl/copts/GENERATED_AbseilCopts.cmake index 46563d41..ba70ef9b 100644 --- a/absl/copts/GENERATED_AbseilCopts.cmake +++ b/absl/copts/GENERATED_AbseilCopts.cmake @@ -13,15 +13,11 @@ list(APPEND ABSL_CLANG_CL_FLAGS ) list(APPEND ABSL_CLANG_CL_TEST_FLAGS - "-Wno-c99-extensions" "-Wno-deprecated-declarations" "-Wno-implicit-int-conversion" - "-Wno-missing-noreturn" "-Wno-missing-prototypes" "-Wno-missing-variable-declarations" - "-Wno-null-conversion" "-Wno-shadow" - "-Wno-shift-sign-overflow" "-Wno-shorten-64-to-32" "-Wno-sign-compare" "-Wno-sign-conversion" @@ -32,7 +28,6 @@ list(APPEND ABSL_CLANG_CL_TEST_FLAGS "-Wno-unused-private-field" "-Wno-unused-template" "-Wno-used-but-marked-unused" - "-Wno-zero-as-null-pointer-constant" "-Wno-gnu-zero-variadic-macro-arguments" ) @@ -55,7 +50,6 @@ list(APPEND ABSL_GCC_FLAGS ) list(APPEND ABSL_GCC_TEST_FLAGS - "-Wno-conversion-null" "-Wno-deprecated-declarations" "-Wno-missing-declarations" "-Wno-sign-compare" @@ -101,15 +95,11 @@ list(APPEND ABSL_LLVM_FLAGS ) list(APPEND ABSL_LLVM_TEST_FLAGS - "-Wno-c99-extensions" "-Wno-deprecated-declarations" "-Wno-implicit-int-conversion" - "-Wno-missing-noreturn" "-Wno-missing-prototypes" "-Wno-missing-variable-declarations" - "-Wno-null-conversion" "-Wno-shadow" - "-Wno-shift-sign-overflow" "-Wno-shorten-64-to-32" "-Wno-sign-compare" "-Wno-sign-conversion" @@ -120,7 +110,6 @@ list(APPEND ABSL_LLVM_TEST_FLAGS "-Wno-unused-private-field" "-Wno-unused-template" "-Wno-used-but-marked-unused" - "-Wno-zero-as-null-pointer-constant" "-Wno-gnu-zero-variadic-macro-arguments" ) diff --git a/absl/copts/GENERATED_copts.bzl b/absl/copts/GENERATED_copts.bzl index 8518b63b..62aab656 100644 --- a/absl/copts/GENERATED_copts.bzl +++ b/absl/copts/GENERATED_copts.bzl @@ -14,15 +14,11 @@ ABSL_CLANG_CL_FLAGS = [ ] ABSL_CLANG_CL_TEST_FLAGS = [ - "-Wno-c99-extensions", "-Wno-deprecated-declarations", "-Wno-implicit-int-conversion", - "-Wno-missing-noreturn", "-Wno-missing-prototypes", "-Wno-missing-variable-declarations", - "-Wno-null-conversion", "-Wno-shadow", - "-Wno-shift-sign-overflow", "-Wno-shorten-64-to-32", "-Wno-sign-compare", "-Wno-sign-conversion", @@ -33,7 +29,6 @@ ABSL_CLANG_CL_TEST_FLAGS = [ "-Wno-unused-private-field", "-Wno-unused-template", "-Wno-used-but-marked-unused", - "-Wno-zero-as-null-pointer-constant", "-Wno-gnu-zero-variadic-macro-arguments", ] @@ -56,7 +51,6 @@ ABSL_GCC_FLAGS = [ ] ABSL_GCC_TEST_FLAGS = [ - "-Wno-conversion-null", "-Wno-deprecated-declarations", "-Wno-missing-declarations", "-Wno-sign-compare", @@ -102,15 +96,11 @@ ABSL_LLVM_FLAGS = [ ] ABSL_LLVM_TEST_FLAGS = [ - "-Wno-c99-extensions", "-Wno-deprecated-declarations", "-Wno-implicit-int-conversion", - "-Wno-missing-noreturn", "-Wno-missing-prototypes", "-Wno-missing-variable-declarations", - "-Wno-null-conversion", "-Wno-shadow", - "-Wno-shift-sign-overflow", "-Wno-shorten-64-to-32", "-Wno-sign-compare", "-Wno-sign-conversion", @@ -121,7 +111,6 @@ ABSL_LLVM_TEST_FLAGS = [ "-Wno-unused-private-field", "-Wno-unused-template", "-Wno-used-but-marked-unused", - "-Wno-zero-as-null-pointer-constant", "-Wno-gnu-zero-variadic-macro-arguments", ] diff --git a/absl/copts/copts.py b/absl/copts/copts.py index 17c467c1..732af9ea 100644 --- a/absl/copts/copts.py +++ b/absl/copts/copts.py @@ -17,15 +17,11 @@ MSVC_BIG_WARNING_FLAGS = [ ] LLVM_TEST_DISABLE_WARNINGS_FLAGS = [ - "-Wno-c99-extensions", "-Wno-deprecated-declarations", "-Wno-implicit-int-conversion", - "-Wno-missing-noreturn", "-Wno-missing-prototypes", "-Wno-missing-variable-declarations", - "-Wno-null-conversion", "-Wno-shadow", - "-Wno-shift-sign-overflow", "-Wno-shorten-64-to-32", "-Wno-sign-compare", "-Wno-sign-conversion", @@ -36,7 +32,6 @@ LLVM_TEST_DISABLE_WARNINGS_FLAGS = [ "-Wno-unused-private-field", "-Wno-unused-template", "-Wno-used-but-marked-unused", - "-Wno-zero-as-null-pointer-constant", # gtest depends on this GNU extension being offered. "-Wno-gnu-zero-variadic-macro-arguments", ] @@ -72,7 +67,6 @@ COPT_VARS = { "-DNOMINMAX", ], "ABSL_GCC_TEST_FLAGS": [ - "-Wno-conversion-null", "-Wno-deprecated-declarations", "-Wno-missing-declarations", "-Wno-sign-compare", diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel index dadc8856..16788ae2 100644 --- a/absl/log/BUILD.bazel +++ b/absl/log/BUILD.bazel @@ -324,6 +324,7 @@ cc_test( "//absl/log/internal:config", "//absl/log/internal:test_matchers", "//absl/strings", + "//absl/strings:str_format", "@com_google_googletest//:gtest_main", ], ) diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt index 09e4ca0c..28d4b519 100644 --- a/absl/log/CMakeLists.txt +++ b/absl/log/CMakeLists.txt @@ -681,6 +681,7 @@ absl_cc_test( absl::log_internal_config absl::log_internal_test_matchers absl::scoped_mock_log + absl::str_format absl::strings GTest::gmock GTest::gtest_main diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h index 37a267c0..992bb630 100644 --- a/absl/log/internal/log_message.h +++ b/absl/log/internal/log_message.h @@ -41,6 +41,7 @@ #include "absl/log/internal/nullguard.h" #include "absl/log/log_entry.h" #include "absl/log/log_sink.h" +#include "absl/strings/internal/has_absl_stringify.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" @@ -153,8 +154,17 @@ class LogMessage { template <int SIZE> LogMessage& operator<<(char (&buf)[SIZE]) ABSL_ATTRIBUTE_NOINLINE; - // Default: uses `ostream` logging to convert `v` to a string. - template <typename T> + // Types that support `AbslStringify()` are serialized that way. + template <typename T, + typename std::enable_if< + strings_internal::HasAbslStringify<T>::value, int>::type = 0> + LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE; + + // Types that don't support `AbslStringify()` but do support streaming into a + // `std::ostream&` are serialized that way. + template <typename T, + typename std::enable_if< + !strings_internal::HasAbslStringify<T>::value, int>::type = 0> LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE; // Note: We explicitly do not support `operator<<` for non-const references @@ -205,12 +215,44 @@ class LogMessage { std::ostream stream_; }; +// Helper class so that `AbslStringify()` can modify the LogMessage. +class StringifySink final { + public: + explicit StringifySink(LogMessage& message) : message_(message) {} + + void Append(size_t count, char ch) { message_ << std::string(count, ch); } + + void Append(absl::string_view v) { message_ << v; } + + // For types that implement `AbslStringify` using `absl::Format()`. + friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) { + sink->Append(v); + } + + private: + LogMessage& message_; +}; + +// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE` +template <typename T, + typename std::enable_if<strings_internal::HasAbslStringify<T>::value, + int>::type> +LogMessage& LogMessage::operator<<(const T& v) { + StringifySink sink(*this); + // Replace with public API. + AbslStringify(sink, v); + return *this; +} + // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE` -template <typename T> +template <typename T, + typename std::enable_if<!strings_internal::HasAbslStringify<T>::value, + int>::type> LogMessage& LogMessage::operator<<(const T& v) { stream_ << log_internal::NullGuard<T>().Guard(v); return *this; } + inline LogMessage& LogMessage::operator<<( std::ostream& (*m)(std::ostream& os)) { stream_ << m; diff --git a/absl/log/log.h b/absl/log/log.h index 5331fdbf..4cd52041 100644 --- a/absl/log/log.h +++ b/absl/log/log.h @@ -132,10 +132,45 @@ // as they would be if streamed into a `std::ostream`, however it should be // noted that their actual type is unspecified. // -// To implement a custom formatting operator for a type you own, define +// To implement a custom formatting operator for a type you own, there are two +// options: `AbslStringify()` or `std::ostream& operator<<(std::ostream&, ...)`. +// It is recommended that users make their types loggable through +// `AbslStringify()` as it is a universal stringification extension that also +// enables `absl::StrFormat` and `absl::StrCat` support. If both +// `AbslStringify()` and `std::ostream& operator<<(std::ostream&, ...)` are +// defined, `AbslStringify()` will be used. +// +// To use the `AbslStringify()` API, define a friend function template in your +// type's namespace with the following signature: +// +// template <typename Sink> +// void AbslStringify(Sink& sink, const UserDefinedType& value); +// +// `Sink` has the same interface as `absl::FormatSink`, but without +// `PutPaddedString()`. +// +// Example: +// +// struct Point { +// template <typename Sink> +// friend void AbslStringify(Sink& sink, const Point& p) { +// absl::Format(&sink, "(%v, %v)", p.x, p.y); +// } +// +// int x; +// int y; +// }; +// +// To use `std::ostream& operator<<(std::ostream&, ...)`, define // `std::ostream& operator<<(std::ostream&, ...)` in your type's namespace (for // ADL) just as you would to stream it to `std::cout`. // +// Currently `AbslStringify()` ignores output manipulators but this is not +// guaranteed behavior and may be subject to change in the future. If you would +// like guaranteed behavior regarding output manipulators, please use +// `std::ostream& operator<<(std::ostream&, ...)` to make custom types loggable +// instead. +// // Those macros that support streaming honor output manipulators and `fmtflag` // changes that output data (e.g. `std::ends`) or control formatting of data // (e.g. `std::hex` and `std::fixed`), however flushing such a stream is diff --git a/absl/log/log_format_test.cc b/absl/log/log_format_test.cc index c629fce7..397c8d0c 100644 --- a/absl/log/log_format_test.cc +++ b/absl/log/log_format_test.cc @@ -32,6 +32,7 @@ #include "absl/log/scoped_mock_log.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/strings/string_view.h" namespace { @@ -865,6 +866,111 @@ TEST(LogFormatTest, CustomNonCopyable) { LOG(INFO) << value; } +struct Point { + template <typename Sink> + friend void AbslStringify(Sink& sink, const Point& p) { + absl::Format(&sink, "(%d, %d)", p.x, p.y); + } + + int x = 10; + int y = 20; +}; + +TEST(LogFormatTest, AbslStringifyExample) { + absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); + + Point p; + + EXPECT_CALL( + test_sink, + Send(AllOf( + TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))), + ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb"))))); + + test_sink.StartCapturingLogs(); + LOG(INFO) << p; +} + +struct PointWithAbslStringifiyAndOstream { + template <typename Sink> + friend void AbslStringify(Sink& sink, + const PointWithAbslStringifiyAndOstream& p) { + absl::Format(&sink, "(%d, %d)", p.x, p.y); + } + + int x = 10; + int y = 20; +}; + +ABSL_ATTRIBUTE_UNUSED std::ostream& operator<<( + std::ostream& os, const PointWithAbslStringifiyAndOstream&) { + return os << "Default to AbslStringify()"; +} + +TEST(LogFormatTest, CustomWithAbslStringifyAndOstream) { + absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); + + PointWithAbslStringifiyAndOstream p; + + EXPECT_CALL( + test_sink, + Send(AllOf( + TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))), + ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb"))))); + + test_sink.StartCapturingLogs(); + LOG(INFO) << p; +} + +struct PointStreamsNothing { + template <typename Sink> + friend void AbslStringify(Sink&, const PointStreamsNothing&) {} + + int x = 10; + int y = 20; +}; + +TEST(LogFormatTest, AbslStringifyStreamsNothing) { + absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); + + PointStreamsNothing p; + + EXPECT_CALL( + test_sink, + Send(AllOf(TextMessage(Eq("77")), TextMessage(Eq(absl::StrCat(p, 77))), + ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "77" })pb"))))); + + test_sink.StartCapturingLogs(); + LOG(INFO) << p << 77; +} + +struct PointMultipleAppend { + template <typename Sink> + friend void AbslStringify(Sink& sink, const PointMultipleAppend& p) { + sink.Append("("); + sink.Append(absl::StrCat(p.x, ", ", p.y, ")")); + } + + int x = 10; + int y = 20; +}; + +TEST(LogFormatTest, AbslStringifyMultipleAppend) { + absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); + + PointMultipleAppend p; + + EXPECT_CALL( + test_sink, + Send(AllOf( + TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))), + ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(" } + value { str: "10, 20)" })pb"))))); + + test_sink.StartCapturingLogs(); + LOG(INFO) << p; +} + TEST(ManipulatorLogFormatTest, BoolAlphaTrue) { absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); @@ -1501,6 +1607,31 @@ TEST(ManipulatorLogFormatTest, CustomClassStreamsNothing) { LOG(INFO) << value << 77; } +struct PointPercentV { + template <typename Sink> + friend void AbslStringify(Sink& sink, const PointPercentV& p) { + absl::Format(&sink, "(%v, %v)", p.x, p.y); + } + + int x = 10; + int y = 20; +}; + +TEST(ManipulatorLogFormatTest, IOManipsDoNotAffectAbslStringify) { + absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected); + + PointPercentV p; + + EXPECT_CALL( + test_sink, + Send(AllOf( + TextMessage(Eq("(10, 20)")), TextMessage(Eq(absl::StrCat(p))), + ENCODED_MESSAGE(EqualsProto(R"pb(value { str: "(10, 20)" })pb"))))); + + test_sink.StartCapturingLogs(); + LOG(INFO) << std::hex << p; +} + // Tests that verify the behavior when more data are streamed into a `LOG` // statement than fit in the buffer. // Structured logging scenario is tested in other unit tests since the output is diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 4794f4ca..12a8d155 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -58,6 +58,7 @@ cc_library( "charconv.h", "escaping.h", "internal/damerau_levenshtein_distance.h", + "internal/has_absl_stringify.h", "internal/string_constant.h", "match.h", "numbers.h", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 87b1a78f..7e91ebf2 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -23,6 +23,7 @@ absl_cc_library( "escaping.h" "internal/damerau_levenshtein_distance.h" "internal/string_constant.h" + "internal/has_absl_stringify.h" "match.h" "numbers.h" "str_cat.h" diff --git a/absl/strings/internal/has_absl_stringify.h b/absl/strings/internal/has_absl_stringify.h new file mode 100644 index 00000000..55a08508 --- /dev/null +++ b/absl/strings/internal/has_absl_stringify.h @@ -0,0 +1,55 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ +#define ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ +#include <string> +#include <type_traits> +#include <utility> + +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace strings_internal { + +// This is an empty class not intended to be used. It exists so that +// `HasAbslStringify` can reference a universal class rather than needing to be +// copied for each new sink. +class UnimplementedSink { + public: + void Append(size_t count, char ch); + + void Append(string_view v); + + // Support `absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v); +}; + +template <typename T, typename = void> +struct HasAbslStringify : std::false_type {}; + +template <typename T> +struct HasAbslStringify< + T, std::enable_if_t<std::is_void<decltype(AbslStringify( + std::declval<strings_internal::UnimplementedSink&>(), + std::declval<const T&>()))>::value>> : std::true_type {}; + +} // namespace strings_internal + +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_HAS_ABSL_STRINGIFY_H_ diff --git a/absl/strings/internal/stringify_sink.h b/absl/strings/internal/stringify_sink.h index 5e326a0b..fc3747bb 100644 --- a/absl/strings/internal/stringify_sink.h +++ b/absl/strings/internal/stringify_sink.h @@ -49,15 +49,6 @@ string_view ExtractStringification(StringifySink& sink, const T& v) { return sink.buffer_; } -template <typename T, typename = void> -struct HasAbslStringify : std::false_type {}; - -template <typename T> -struct HasAbslStringify<T, std::enable_if_t<std::is_void<decltype(AbslStringify( - std::declval<strings_internal::StringifySink&>(), - std::declval<const T&>()))>::value>> - : std::true_type {}; - } // namespace strings_internal ABSL_NAMESPACE_END diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index 1a37faae..00af84ea 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -95,6 +95,7 @@ #include <vector> #include "absl/base/port.h" +#include "absl/strings/internal/has_absl_stringify.h" #include "absl/strings/internal/stringify_sink.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" diff --git a/absl/synchronization/lifetime_test.cc b/absl/synchronization/lifetime_test.cc index 1067a331..e6274232 100644 --- a/absl/synchronization/lifetime_test.cc +++ b/absl/synchronization/lifetime_test.cc @@ -125,7 +125,8 @@ class OnDestruction { // These tests require that the compiler correctly supports C++11 constant // initialization... but MSVC has a known regression since v19.10 till v19.25: // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html -#if defined(__clang__) || !(defined(_MSC_VER) && _MSC_VER > 1900 && _MSC_VER < 1925) +#if defined(__clang__) || \ + !(defined(_MSC_VER) && _MSC_VER > 1900 && _MSC_VER < 1925) // kConstInit // Test early usage. (Declaration comes first; definitions must appear after // the test runner.) |