diff options
Diffstat (limited to 'absl/strings')
-rw-r--r-- | absl/strings/BUILD.bazel | 1 | ||||
-rw-r--r-- | absl/strings/CMakeLists.txt | 1 | ||||
-rw-r--r-- | absl/strings/internal/stringify_sink.h | 68 | ||||
-rw-r--r-- | absl/strings/str_cat.h | 103 | ||||
-rw-r--r-- | absl/strings/substitute.h | 31 | ||||
-rw-r--r-- | absl/strings/substitute_test.cc | 23 |
6 files changed, 145 insertions, 82 deletions
diff --git a/absl/strings/BUILD.bazel b/absl/strings/BUILD.bazel index 5b12c010..c6989816 100644 --- a/absl/strings/BUILD.bazel +++ b/absl/strings/BUILD.bazel @@ -42,6 +42,7 @@ cc_library( "internal/stl_type_traits.h", "internal/str_join_internal.h", "internal/str_split_internal.h", + "internal/stringify_sink.h", "match.cc", "numbers.cc", "str_cat.cc", diff --git a/absl/strings/CMakeLists.txt b/absl/strings/CMakeLists.txt index 01f86184..fe82e1df 100644 --- a/absl/strings/CMakeLists.txt +++ b/absl/strings/CMakeLists.txt @@ -41,6 +41,7 @@ absl_cc_library( "internal/charconv_parse.h" "internal/memutil.cc" "internal/memutil.h" + "internal/stringify_sink.h" "internal/stl_type_traits.h" "internal/str_join_internal.h" "internal/str_split_internal.h" diff --git a/absl/strings/internal/stringify_sink.h b/absl/strings/internal/stringify_sink.h new file mode 100644 index 00000000..a83f70e4 --- /dev/null +++ b/absl/strings/internal/stringify_sink.h @@ -0,0 +1,68 @@ +// 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_STRINGIFY_SINK_H_ +#define ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ + +#include <string> +#include <type_traits> +#include <utility> + +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN + +namespace strings_internal { +class StringifySink { + public: + void Append(size_t count, char ch); + + void Append(string_view v); + + bool PutPaddedString(string_view v, int width, int precision, bool left); + + // Support `absl::Format(&sink, format, args...)`. + friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) { + sink->Append(v); + } + + private: + template <typename T> + friend string_view ExtractStringification(StringifySink& sink, const T& v); + + std::string buffer_; +}; + +template <typename T> +string_view ExtractStringification(StringifySink& sink, const T& v) { + AbslStringify(sink, 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 +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_STRINGIFY_SINK_H_ diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h index 8a63be0d..1a37faae 100644 --- a/absl/strings/str_cat.h +++ b/absl/strings/str_cat.h @@ -48,53 +48,22 @@ // `StrCat()` or `StrAppend()`. You may specify a minimum hex field width using // a `PadSpec` enum. // -// ----------------------------------------------------------------------------- - -#ifndef ABSL_STRINGS_STR_CAT_H_ -#define ABSL_STRINGS_STR_CAT_H_ - -#include <array> -#include <cstdint> -#include <string> -#include <type_traits> -#include <utility> -#include <vector> - -#include "absl/base/port.h" -#include "absl/strings/numbers.h" -#include "absl/strings/string_view.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN - -namespace strings_internal { -// AlphaNumBuffer allows a way to pass a string to StrCat without having to do -// memory allocation. It is simply a pair of a fixed-size character array, and -// a size. Please don't use outside of absl, yet. -template <size_t max_size> -struct AlphaNumBuffer { - std::array<char, max_size> data; - size_t size; -}; - -//------------------------------------------------------------------------------ -// StrCat Extension -//------------------------------------------------------------------------------ -// -// AbslStringify() +// User-defined types can be formatted with the `AbslStringify()` customization +// point. The API relies on detecting an overload in the user-defined type's +// namespace of a free (non-member) `AbslStringify()` function as a definition +// (typically declared as a friend and implemented in-line. +// with the following signature: // -// A simple customization API for formatting user-defined types using -// absl::StrCat(). The API relies on detecting an overload in the -// user-defined type's namespace of a free (non-member) `AbslStringify()` -// function as a friend definition with the following signature: +// class MyClass { ... }; // // template <typename Sink> -// void AbslStringify(Sink& sink, const X& value); +// void AbslStringify(Sink& sink, const MyClass& value); // // An `AbslStringify()` overload for a type should only be declared in the same // file and namespace as said type. // -// Note that AbslStringify() also supports use with absl::StrFormat(). +// Note that `AbslStringify()` also supports use with `absl::StrFormat()` and +// `absl::Substitute()`. // // Example: // @@ -113,33 +82,36 @@ struct AlphaNumBuffer { // int x; // int y; // }; +// ----------------------------------------------------------------------------- -class StringifySink { - public: - void Append(size_t count, char ch); - - void Append(string_view v); +#ifndef ABSL_STRINGS_STR_CAT_H_ +#define ABSL_STRINGS_STR_CAT_H_ - bool PutPaddedString(string_view v, int width, int precision, bool left); +#include <array> +#include <cstdint> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> - // Support `absl::Format(&sink, format, args...)`. - friend void AbslFormatFlush(StringifySink* sink, absl::string_view v) { - sink->Append(v); - } +#include "absl/base/port.h" +#include "absl/strings/internal/stringify_sink.h" +#include "absl/strings/numbers.h" +#include "absl/strings/string_view.h" - template <typename T> - friend string_view ExtractStringification(StringifySink& sink, const T& v); +namespace absl { +ABSL_NAMESPACE_BEGIN - private: - std::string buffer_; +namespace strings_internal { +// AlphaNumBuffer allows a way to pass a string to StrCat without having to do +// memory allocation. It is simply a pair of a fixed-size character array, and +// a size. Please don't use outside of absl, yet. +template <size_t max_size> +struct AlphaNumBuffer { + std::array<char, max_size> data; + size_t size; }; -template <typename T> -string_view ExtractStringification(StringifySink& sink, const T& v) { - AbslStringify(sink, v); - return sink.buffer_; -} - } // namespace strings_internal // Enum that specifies the number of significant digits to return in a `Hex` or @@ -272,15 +244,6 @@ struct Dec { // `StrAppend()`, providing efficient conversion of numeric, boolean, and // hexadecimal values (through the `Hex` type) into strings. -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 {}; - class AlphaNum { public: // No bool ctor -- bools convert to an integral type. @@ -329,7 +292,7 @@ class AlphaNum { AlphaNum(absl::string_view pc) : piece_(pc) {} // NOLINT(runtime/explicit) template <typename T, typename = typename std::enable_if< - HasAbslStringify<T>::value>::type> + strings_internal::HasAbslStringify<T>::value>::type> AlphaNum( // NOLINT(runtime/explicit) const T& v, // NOLINT(runtime/explicit) strings_internal::StringifySink&& sink = {}) // NOLINT(runtime/explicit) diff --git a/absl/strings/substitute.h b/absl/strings/substitute.h index 692fd03c..5c3f6eff 100644 --- a/absl/strings/substitute.h +++ b/absl/strings/substitute.h @@ -55,6 +55,8 @@ // * bool (Printed as "true" or "false") // * pointer types other than char* (Printed as "0x<lower case hex string>", // except that null is printed as "NULL") +// * user-defined types via the `AbslStringify()` customization point. See the +// documentation for `absl::StrCat` for an explanation on how to use this. // // If an invalid format string is provided, Substitute returns an empty string // and SubstituteAndAppend does not change the provided output string. @@ -79,6 +81,7 @@ #include "absl/base/port.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" +#include "absl/strings/internal/stringify_sink.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" @@ -102,14 +105,14 @@ class Arg { // Overloads for string-y things // // Explicitly overload `const char*` so the compiler doesn't cast to `bool`. - Arg(const char* value) // NOLINT(runtime/explicit) + Arg(const char* value) // NOLINT(google-explicit-constructor) : piece_(absl::NullSafeStringView(value)) {} template <typename Allocator> Arg( // NOLINT const std::basic_string<char, std::char_traits<char>, Allocator>& value) noexcept : piece_(value) {} - Arg(absl::string_view value) // NOLINT(runtime/explicit) + Arg(absl::string_view value) // NOLINT(google-explicit-constructor) : piece_(value) {} // Overloads for primitives @@ -119,7 +122,7 @@ class Arg { // probably using them as 8-bit integers and would probably prefer an integer // representation. However, we can't really know, so we make the caller decide // what to do. - Arg(char value) // NOLINT(runtime/explicit) + Arg(char value) // NOLINT(google-explicit-constructor) : piece_(scratch_, 1) { scratch_[0] = value; } @@ -133,12 +136,12 @@ class Arg { static_cast<size_t>( numbers_internal::FastIntToBuffer(value, scratch_) - scratch_)) {} - Arg(int value) // NOLINT(runtime/explicit) + Arg(int value) // NOLINT(google-explicit-constructor) : piece_(scratch_, static_cast<size_t>( numbers_internal::FastIntToBuffer(value, scratch_) - scratch_)) {} - Arg(unsigned int value) // NOLINT(runtime/explicit) + Arg(unsigned int value) // NOLINT(google-explicit-constructor) : piece_(scratch_, static_cast<size_t>( numbers_internal::FastIntToBuffer(value, scratch_) - @@ -163,17 +166,23 @@ class Arg { static_cast<size_t>( numbers_internal::FastIntToBuffer(value, scratch_) - scratch_)) {} - Arg(float value) // NOLINT(runtime/explicit) + Arg(float value) // NOLINT(google-explicit-constructor) : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) { } - Arg(double value) // NOLINT(runtime/explicit) + Arg(double value) // NOLINT(google-explicit-constructor) : piece_(scratch_, numbers_internal::SixDigitsToBuffer(value, scratch_)) { } - Arg(bool value) // NOLINT(runtime/explicit) + Arg(bool value) // NOLINT(google-explicit-constructor) : piece_(value ? "true" : "false") {} - Arg(Hex hex); // NOLINT(runtime/explicit) - Arg(Dec dec); // NOLINT(runtime/explicit) + template <typename T, typename = typename std::enable_if< + strings_internal::HasAbslStringify<T>::value>::type> + Arg( // NOLINT(google-explicit-constructor) + const T& v, strings_internal::StringifySink&& sink = {}) + : piece_(strings_internal::ExtractStringification(sink, v)) {} + + Arg(Hex hex); // NOLINT(google-explicit-constructor) + Arg(Dec dec); // NOLINT(google-explicit-constructor) // vector<bool>::reference and const_reference require special help to convert // to `Arg` because it requires two user defined conversions. @@ -188,7 +197,7 @@ class Arg { // `void*` values, with the exception of `char*`, are printed as // "0x<hex value>". However, in the case of `nullptr`, "NULL" is printed. - Arg(const void* value); // NOLINT(runtime/explicit) + Arg(const void* value); // NOLINT(google-explicit-constructor) // Normal enums are already handled by the integer formatters. // This overload matches only scoped enums. diff --git a/absl/strings/substitute_test.cc b/absl/strings/substitute_test.cc index 9e6b9403..9f04545f 100644 --- a/absl/strings/substitute_test.cc +++ b/absl/strings/substitute_test.cc @@ -22,6 +22,16 @@ namespace { +struct MyStruct { + template <typename Sink> + friend void AbslStringify(Sink& sink, const MyStruct& s) { + sink.Append("MyStruct{.value = "); + sink.Append(absl::StrCat(s.value)); + sink.Append("}"); + } + int value; +}; + TEST(SubstituteTest, Substitute) { // Basic. EXPECT_EQ("Hello, world!", absl::Substitute("$0, $1!", "Hello", "world")); @@ -70,7 +80,7 @@ TEST(SubstituteTest, Substitute) { // Volatile Pointer. // Like C++ streamed I/O, such pointers implicitly become bool volatile int vol = 237; - volatile int *volatile volptr = &vol; + volatile int* volatile volptr = &vol; str = absl::Substitute("$0", volptr); EXPECT_EQ("true", str); @@ -128,6 +138,11 @@ TEST(SubstituteTest, Substitute) { const char* null_cstring = nullptr; EXPECT_EQ("Text: ''", absl::Substitute("Text: '$0'", null_cstring)); + + MyStruct s1 = MyStruct{17}; + MyStruct s2 = MyStruct{1043}; + EXPECT_EQ("MyStruct{.value = 17}, MyStruct{.value = 1043}", + absl::Substitute("$0, $1", s1, s2)); } TEST(SubstituteTest, SubstituteAndAppend) { @@ -171,6 +186,12 @@ TEST(SubstituteTest, SubstituteAndAppend) { absl::SubstituteAndAppend(&str, "$0 $1 $2 $3 $4 $5 $6 $7 $8 $9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j"); EXPECT_EQ("a b c d e f g h i j", str); + + str.clear(); + MyStruct s1 = MyStruct{17}; + MyStruct s2 = MyStruct{1043}; + absl::SubstituteAndAppend(&str, "$0, $1", s1, s2); + EXPECT_EQ("MyStruct{.value = 17}, MyStruct{.value = 1043}", str); } TEST(SubstituteTest, VectorBoolRef) { |