Skip to content

Commit 5d365d3

Browse files
ckennellycopybara-github
authored andcommitted
absl/strings: Prepare helper for printing objects to string representations.
PiperOrigin-RevId: 847935770 Change-Id: I7f96940e5ba11d6a602d34e7dc3dbfde112bb142
1 parent 1037021 commit 5d365d3

File tree

8 files changed

+1413
-0
lines changed

8 files changed

+1413
-0
lines changed

CMake/AbseilDll.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ set(ABSL_INTERNAL_DLL_FILES
331331
"strings/internal/cordz_update_tracker.h"
332332
"strings/internal/damerau_levenshtein_distance.h"
333333
"strings/internal/damerau_levenshtein_distance.cc"
334+
"strings/internal/generic_printer.cc"
335+
"strings/internal/generic_printer.h"
336+
"strings/internal/generic_printer_internal.h"
334337
"strings/internal/stl_type_traits.h"
335338
"strings/internal/string_constant.h"
336339
"strings/internal/stringify_sink.h"

absl/log/internal/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,9 @@ cc_library(
554554
hdrs = ["container.h"],
555555
copts = ABSL_DEFAULT_COPTS,
556556
linkopts = ABSL_DEFAULT_LINKOPTS,
557+
visibility = [
558+
"//absl:__subpackages__",
559+
],
557560
deps = [
558561
"//absl/base:config",
559562
"//absl/meta:requires",

absl/strings/BUILD.bazel

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,3 +1551,43 @@ cc_test(
15511551
"@googletest//:gtest_main",
15521552
],
15531553
)
1554+
1555+
cc_library(
1556+
name = "generic_printer",
1557+
srcs = [
1558+
"internal/generic_printer.cc",
1559+
"internal/generic_printer_internal.h",
1560+
],
1561+
hdrs = ["internal/generic_printer.h"],
1562+
copts = ABSL_DEFAULT_COPTS,
1563+
linkopts = ABSL_DEFAULT_LINKOPTS,
1564+
visibility = [
1565+
"//absl:__subpackages__",
1566+
],
1567+
deps = [
1568+
":str_format",
1569+
":strings",
1570+
"//absl/base:config",
1571+
"//absl/log/internal:container",
1572+
"//absl/meta:requires",
1573+
],
1574+
)
1575+
1576+
cc_test(
1577+
name = "generic_printer_test",
1578+
srcs = ["internal/generic_printer_test.cc"],
1579+
copts = ABSL_TEST_COPTS,
1580+
linkopts = ABSL_DEFAULT_LINKOPTS,
1581+
deps = [
1582+
":generic_printer",
1583+
":strings",
1584+
"//absl/base:config",
1585+
"//absl/base:core_headers",
1586+
"//absl/container:flat_hash_map",
1587+
"//absl/log",
1588+
"//absl/status",
1589+
"//absl/status:statusor",
1590+
"@googletest//:gtest",
1591+
"@googletest//:gtest_main",
1592+
],
1593+
)

absl/strings/CMakeLists.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,3 +1256,40 @@ absl_cc_test(
12561256
absl::strings
12571257
GTest::gmock_main
12581258
)
1259+
1260+
absl_cc_library(
1261+
NAME
1262+
generic_printer_internal
1263+
SRCS
1264+
"internal/generic_printer.cc"
1265+
"internal/generic_printer_internal.h"
1266+
HDRS
1267+
"internal/generic_printer.h"
1268+
COPTS
1269+
${ABSL_DEFAULT_COPTS}
1270+
DEPS
1271+
absl::config
1272+
absl::strings
1273+
absl::str_format
1274+
absl::log_internal_container
1275+
absl::requires_internal
1276+
)
1277+
1278+
absl_cc_test(
1279+
NAME
1280+
generic_printer_test
1281+
SRCS
1282+
"internal/generic_printer_test.cc"
1283+
COPTS
1284+
${ABSL_TEST_COPTS}
1285+
DEPS
1286+
absl::base
1287+
absl::config
1288+
absl::flat_hash_map
1289+
absl::generic_printer_internal
1290+
absl::log
1291+
absl::status
1292+
absl::statusor
1293+
absl::strings
1294+
GTest::gmock_main
1295+
)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2025 The Abseil Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "absl/strings/internal/generic_printer.h"
16+
17+
#include <cstddef>
18+
#include <cstdlib>
19+
#include <ostream>
20+
#include <string>
21+
22+
#include "absl/base/config.h"
23+
#include "absl/strings/ascii.h"
24+
#include "absl/strings/escaping.h"
25+
#include "absl/strings/str_format.h"
26+
27+
namespace absl {
28+
ABSL_NAMESPACE_BEGIN
29+
namespace internal_generic_printer {
30+
31+
// Out-of-line helper for PrintAsStringWithEscaping.
32+
std::ostream& PrintEscapedString(std::ostream& os, absl::string_view v) {
33+
return os << "\"" << absl::CHexEscape(v) << "\"";
34+
}
35+
36+
// Retuns a string representation of 'v', shortened if possible.
37+
template <class T, class F>
38+
std::string TryShorten(T v, F strtox) {
39+
std::string printed =
40+
absl::StrFormat("%.*g", std::numeric_limits<T>::max_digits10 / 2, v);
41+
T parsed = strtox(printed.data());
42+
if (parsed != v) {
43+
printed =
44+
absl::StrFormat("%.*g", std::numeric_limits<T>::max_digits10 + 1, v);
45+
}
46+
return printed;
47+
}
48+
49+
// Out-of-line helpers for floating point values. These don't necessarily
50+
// ensure that values are precise, but rather that they are wide enough to
51+
// represent distinct values. go/c++17std/numeric.limits.members.html
52+
std::ostream& PrintPreciseFP(std::ostream& os, float v) {
53+
return os << TryShorten(v, [](const char* buf) {
54+
char* unused;
55+
return std::strtof(buf, &unused);
56+
}) << "f";
57+
}
58+
std::ostream& PrintPreciseFP(std::ostream& os, double v) {
59+
return os << TryShorten(v, [](const char* buf) {
60+
char* unused;
61+
return std::strtod(buf, &unused);
62+
});
63+
}
64+
std::ostream& PrintPreciseFP(std::ostream& os, long double v) {
65+
return os << TryShorten(v, [](const char* buf) {
66+
char* unused;
67+
return std::strtold(buf, &unused);
68+
}) << "L";
69+
}
70+
71+
// Prints a nibble of 'v' in hexadecimal.
72+
inline char hexnib(int v) {
73+
return static_cast<char>((v < 10 ? '0' : ('a' - 10)) + v);
74+
}
75+
76+
template <typename T>
77+
static std::ostream& PrintCharImpl(std::ostream& os, T v) {
78+
// Specialization for chars: print as 'c' if printable, otherwise
79+
// hex-escaped.
80+
return (absl::ascii_isprint(static_cast<unsigned char>(v))
81+
? (os << (v == '\'' ? "'\\" : "'") << v)
82+
: (os << "'\\x" << hexnib((v >> 4) & 0xf) << hexnib(v & 0xf)))
83+
<< "' (0x" << hexnib((v >> 4) & 0xf) << hexnib(v & 0xf) << " "
84+
<< static_cast<int>(v) << ")";
85+
}
86+
87+
std::ostream& PrintChar(std::ostream& os, char c) {
88+
return PrintCharImpl(os, c);
89+
}
90+
91+
std::ostream& PrintChar(std::ostream& os, signed char c) {
92+
return PrintCharImpl(os, c);
93+
}
94+
95+
std::ostream& PrintChar(std::ostream& os, unsigned char c) {
96+
return PrintCharImpl(os, c);
97+
}
98+
99+
std::ostream& PrintByte(std::ostream& os, std::byte b) {
100+
auto v = std::to_integer<int>(b);
101+
os << "0x" << hexnib((v >> 4) & 0xf) << hexnib(v & 0xf);
102+
return os;
103+
}
104+
105+
} // namespace internal_generic_printer
106+
ABSL_NAMESPACE_END
107+
} // namespace absl
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2025 The Abseil Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_H_
16+
#define ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_H_
17+
18+
#include "absl/strings/internal/generic_printer_internal.h" // IWYU pragma: export
19+
20+
// Helpers for printing objects in generic code.
21+
//
22+
// These functions help with streaming in generic code. It may be desirable, for
23+
// example, to log values from generic functions; however, operator<< may not be
24+
// overloaded for all types.
25+
//
26+
// The helpers in this library use, in order of precedence:
27+
//
28+
// 1. std::string and std::string_view are quoted and escaped. (The specific
29+
// format is not guaranteed to be stable.)
30+
// 2. A defined AbslStringify() method.
31+
// 3. absl::log_internal::LogContainer, if the object is a STL container.
32+
// 4. For std::tuple, std::pair, and std::optional, recursively calls
33+
// GenericPrint for each element.
34+
// 5. Floating point values are printed with enough precision for round-trip.
35+
// 6. char values are quoted, with non-printable values escaped, and the
36+
// char's numeric value is included.
37+
// 7. A defined operator<< overload (which should be found by ADL).
38+
// 8. A defined DebugString() const method.
39+
// 9. A fallback value with basic information otherwise.
40+
//
41+
// Note that the fallback value means that if no formatting conversion is
42+
// defined, you will not see a compile-time error. This also means that
43+
// GenericPrint() can safely be used in generic template contexts, and can
44+
// format any types needed (even though the output will vary).
45+
//
46+
// Example usage:
47+
//
48+
// // All values after GenericPrint() are formatted:
49+
// LOG(INFO) << GenericPrint()
50+
// << int_var // <- printed normally
51+
// << float_var // <- sufficient precision for round-trip
52+
// << " unchanged literal text ";
53+
//
54+
// // Just one value is formatted:
55+
// LOG(INFO) << GenericPrint(string("this is quoted and escaped\t\n"))
56+
// << GenericPrint("but not this, ");
57+
// << string("and not this.");
58+
//
59+
// To make a type loggable with GenericPrint, prefer defining operator<< as a
60+
// friend function. For example:
61+
//
62+
// class TypeToLog {
63+
// public:
64+
// string ToString() const; // Many types already implement this.
65+
// // Define out-of-line if it is complex.
66+
// friend std::ostream& operator<<(std::ostream& os, const TypeToLog& v) {
67+
// return os << v.ToString(); // OK to define in-line instead, if simple.
68+
// }
69+
// };
70+
//
71+
// (Defining operator<< as an inline friend free function allows it to be found
72+
// by Argument-Dependent Lookup, or ADL, which is the mechanism typically used
73+
// for operator overload resolution. An inline friend function is the tightest
74+
// scope possible for overloading the left-hand side of an operator.)
75+
76+
#include <ostream>
77+
#include <utility>
78+
79+
namespace absl {
80+
ABSL_NAMESPACE_BEGIN
81+
namespace strings_internal {
82+
83+
// Helper for logging values in generic code.
84+
//
85+
// Example usage:
86+
//
87+
// template <typename T>
88+
// void GenericFunction() {
89+
// T v1, v2;
90+
// VLOG(1) << GenericPrint() << v1 << v2; // GenericPrint everything
91+
// VLOG(1) << GenericPrint(v1) << v2; // GenericPrint just v1
92+
// }
93+
//
94+
inline constexpr internal_generic_printer::GenericPrintAdapterFactory
95+
GenericPrint{};
96+
97+
// Generic printer type: this class can be used, for example, as a template
98+
// argument to allow users to provide alternative printing strategies.
99+
//
100+
// For example, to allow callers to provide a custom strategy:
101+
//
102+
// template <typename T, typename PrinterT = GenericPrinter<T>>
103+
// void GenericFunction() {
104+
// T value;
105+
// VLOG(1) << PrinterT{value};
106+
// }
107+
//
108+
template <typename T>
109+
using GenericPrinter = internal_generic_printer::GenericPrinter<T>;
110+
111+
} // namespace strings_internal
112+
ABSL_NAMESPACE_END
113+
} // namespace absl
114+
115+
#endif // ABSL_STRINGS_INTERNAL_GENERIC_PRINTER_H_

0 commit comments

Comments
 (0)