blob: 31dbf672f0b602e51e2101660338094dd71dabfb [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001//
2// Copyright 2017 The Abseil Authors.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// https://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17// This file declares INTERNAL parts of the Join API that are inlined/templated
18// or otherwise need to be available at compile time. The main abstractions
19// defined in this file are:
20//
21// - A handful of default Formatters
22// - JoinAlgorithm() overloads
23// - JoinRange() overloads
24// - JoinTuple()
25//
26// DO NOT INCLUDE THIS FILE DIRECTLY. Use this file by including
27// absl/strings/str_join.h
28//
29// IWYU pragma: private, include "absl/strings/str_join.h"
30
31#ifndef ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
32#define ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_
33
34#include <cstring>
35#include <iterator>
36#include <memory>
37#include <string>
38#include <type_traits>
39#include <utility>
40
41#include "absl/strings/internal/ostringstream.h"
42#include "absl/strings/internal/resize_uninitialized.h"
43#include "absl/strings/str_cat.h"
44
45namespace absl {
Austin Schuhb4691e92020-12-31 12:37:18 -080046ABSL_NAMESPACE_BEGIN
Austin Schuh36244a12019-09-21 17:52:38 -070047namespace strings_internal {
48
49//
50// Formatter objects
51//
52// The following are implementation classes for standard Formatter objects. The
53// factory functions that users will call to create and use these formatters are
54// defined and documented in strings/join.h.
55//
56
57// The default formatter. Converts alpha-numeric types to strings.
58struct AlphaNumFormatterImpl {
59 // This template is needed in order to support passing in a dereferenced
60 // vector<bool>::iterator
61 template <typename T>
62 void operator()(std::string* out, const T& t) const {
63 StrAppend(out, AlphaNum(t));
64 }
65
66 void operator()(std::string* out, const AlphaNum& t) const {
67 StrAppend(out, t);
68 }
69};
70
71// A type that's used to overload the JoinAlgorithm() function (defined below)
72// for ranges that do not require additional formatting (e.g., a range of
73// strings).
74
75struct NoFormatter : public AlphaNumFormatterImpl {};
76
77// Formats types to strings using the << operator.
78class StreamFormatterImpl {
79 public:
80 // The method isn't const because it mutates state. Making it const will
81 // render StreamFormatterImpl thread-hostile.
82 template <typename T>
83 void operator()(std::string* out, const T& t) {
84 // The stream is created lazily to avoid paying the relatively high cost
85 // of its construction when joining an empty range.
86 if (strm_) {
87 strm_->clear(); // clear the bad, fail and eof bits in case they were set
88 strm_->str(out);
89 } else {
90 strm_.reset(new strings_internal::OStringStream(out));
91 }
92 *strm_ << t;
93 }
94
95 private:
96 std::unique_ptr<strings_internal::OStringStream> strm_;
97};
98
99// Formats a std::pair<>. The 'first' member is formatted using f1_ and the
100// 'second' member is formatted using f2_. sep_ is the separator.
101template <typename F1, typename F2>
102class PairFormatterImpl {
103 public:
104 PairFormatterImpl(F1 f1, absl::string_view sep, F2 f2)
105 : f1_(std::move(f1)), sep_(sep), f2_(std::move(f2)) {}
106
107 template <typename T>
108 void operator()(std::string* out, const T& p) {
109 f1_(out, p.first);
110 out->append(sep_);
111 f2_(out, p.second);
112 }
113
114 template <typename T>
115 void operator()(std::string* out, const T& p) const {
116 f1_(out, p.first);
117 out->append(sep_);
118 f2_(out, p.second);
119 }
120
121 private:
122 F1 f1_;
123 std::string sep_;
124 F2 f2_;
125};
126
127// Wraps another formatter and dereferences the argument to operator() then
128// passes the dereferenced argument to the wrapped formatter. This can be
129// useful, for example, to join a std::vector<int*>.
130template <typename Formatter>
131class DereferenceFormatterImpl {
132 public:
133 DereferenceFormatterImpl() : f_() {}
134 explicit DereferenceFormatterImpl(Formatter&& f)
135 : f_(std::forward<Formatter>(f)) {}
136
137 template <typename T>
138 void operator()(std::string* out, const T& t) {
139 f_(out, *t);
140 }
141
142 template <typename T>
143 void operator()(std::string* out, const T& t) const {
144 f_(out, *t);
145 }
146
147 private:
148 Formatter f_;
149};
150
151// DefaultFormatter<T> is a traits class that selects a default Formatter to use
152// for the given type T. The ::Type member names the Formatter to use. This is
153// used by the strings::Join() functions that do NOT take a Formatter argument,
154// in which case a default Formatter must be chosen.
155//
156// AlphaNumFormatterImpl is the default in the base template, followed by
157// specializations for other types.
158template <typename ValueType>
159struct DefaultFormatter {
160 typedef AlphaNumFormatterImpl Type;
161};
162template <>
163struct DefaultFormatter<const char*> {
164 typedef AlphaNumFormatterImpl Type;
165};
166template <>
167struct DefaultFormatter<char*> {
168 typedef AlphaNumFormatterImpl Type;
169};
170template <>
171struct DefaultFormatter<std::string> {
172 typedef NoFormatter Type;
173};
174template <>
175struct DefaultFormatter<absl::string_view> {
176 typedef NoFormatter Type;
177};
178template <typename ValueType>
179struct DefaultFormatter<ValueType*> {
180 typedef DereferenceFormatterImpl<typename DefaultFormatter<ValueType>::Type>
181 Type;
182};
183
184template <typename ValueType>
185struct DefaultFormatter<std::unique_ptr<ValueType>>
186 : public DefaultFormatter<ValueType*> {};
187
188//
189// JoinAlgorithm() functions
190//
191
192// The main joining algorithm. This simply joins the elements in the given
193// iterator range, each separated by the given separator, into an output string,
194// and formats each element using the provided Formatter object.
195template <typename Iterator, typename Formatter>
196std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
197 Formatter&& f) {
198 std::string result;
199 absl::string_view sep("");
200 for (Iterator it = start; it != end; ++it) {
201 result.append(sep.data(), sep.size());
202 f(&result, *it);
203 sep = s;
204 }
205 return result;
206}
207
208// A joining algorithm that's optimized for a forward iterator range of
209// string-like objects that do not need any additional formatting. This is to
210// optimize the common case of joining, say, a std::vector<string> or a
211// std::vector<absl::string_view>.
212//
213// This is an overload of the previous JoinAlgorithm() function. Here the
214// Formatter argument is of type NoFormatter. Since NoFormatter is an internal
215// type, this overload is only invoked when strings::Join() is called with a
216// range of string-like objects (e.g., std::string, absl::string_view), and an
217// explicit Formatter argument was NOT specified.
218//
219// The optimization is that the needed space will be reserved in the output
220// string to avoid the need to resize while appending. To do this, the iterator
221// range will be traversed twice: once to calculate the total needed size, and
222// then again to copy the elements and delimiters to the output string.
223template <typename Iterator,
224 typename = typename std::enable_if<std::is_convertible<
225 typename std::iterator_traits<Iterator>::iterator_category,
226 std::forward_iterator_tag>::value>::type>
227std::string JoinAlgorithm(Iterator start, Iterator end, absl::string_view s,
228 NoFormatter) {
229 std::string result;
230 if (start != end) {
231 // Sums size
232 size_t result_size = start->size();
233 for (Iterator it = start; ++it != end;) {
234 result_size += s.size();
235 result_size += it->size();
236 }
237
238 if (result_size > 0) {
239 STLStringResizeUninitialized(&result, result_size);
240
241 // Joins strings
242 char* result_buf = &*result.begin();
243 memcpy(result_buf, start->data(), start->size());
244 result_buf += start->size();
245 for (Iterator it = start; ++it != end;) {
246 memcpy(result_buf, s.data(), s.size());
247 result_buf += s.size();
248 memcpy(result_buf, it->data(), it->size());
249 result_buf += it->size();
250 }
251 }
252 }
253
254 return result;
255}
256
257// JoinTupleLoop implements a loop over the elements of a std::tuple, which
258// are heterogeneous. The primary template matches the tuple interior case. It
259// continues the iteration after appending a separator (for nonzero indices)
260// and formatting an element of the tuple. The specialization for the I=N case
261// matches the end-of-tuple, and terminates the iteration.
262template <size_t I, size_t N>
263struct JoinTupleLoop {
264 template <typename Tup, typename Formatter>
265 void operator()(std::string* out, const Tup& tup, absl::string_view sep,
266 Formatter&& fmt) {
267 if (I > 0) out->append(sep.data(), sep.size());
268 fmt(out, std::get<I>(tup));
269 JoinTupleLoop<I + 1, N>()(out, tup, sep, fmt);
270 }
271};
272template <size_t N>
273struct JoinTupleLoop<N, N> {
274 template <typename Tup, typename Formatter>
275 void operator()(std::string*, const Tup&, absl::string_view, Formatter&&) {}
276};
277
278template <typename... T, typename Formatter>
279std::string JoinAlgorithm(const std::tuple<T...>& tup, absl::string_view sep,
280 Formatter&& fmt) {
281 std::string result;
282 JoinTupleLoop<0, sizeof...(T)>()(&result, tup, sep, fmt);
283 return result;
284}
285
286template <typename Iterator>
287std::string JoinRange(Iterator first, Iterator last,
288 absl::string_view separator) {
289 // No formatter was explicitly given, so a default must be chosen.
290 typedef typename std::iterator_traits<Iterator>::value_type ValueType;
291 typedef typename DefaultFormatter<ValueType>::Type Formatter;
292 return JoinAlgorithm(first, last, separator, Formatter());
293}
294
295template <typename Range, typename Formatter>
296std::string JoinRange(const Range& range, absl::string_view separator,
297 Formatter&& fmt) {
298 using std::begin;
299 using std::end;
300 return JoinAlgorithm(begin(range), end(range), separator, fmt);
301}
302
303template <typename Range>
304std::string JoinRange(const Range& range, absl::string_view separator) {
305 using std::begin;
306 using std::end;
307 return JoinRange(begin(range), end(range), separator);
308}
309
310} // namespace strings_internal
Austin Schuhb4691e92020-12-31 12:37:18 -0800311ABSL_NAMESPACE_END
Austin Schuh36244a12019-09-21 17:52:38 -0700312} // namespace absl
313
314#endif // ABSL_STRINGS_INTERNAL_STR_JOIN_INTERNAL_H_