openMSX
strCat.hh
Go to the documentation of this file.
1#ifndef STRCAT_HH
2#define STRCAT_HH
3
4#include "TemporaryString.hh"
5#include "ranges.hh"
6#include "xrange.hh"
7#include "zstring_view.hh"
8#include <array>
9#include <climits>
10#include <limits>
11#include <span>
12#include <sstream>
13#include <string>
14#include <string_view>
15#include <tuple>
16#include <utility>
17
18// strCat and strAppend()
19//
20// Inspired by google's absl::StrCat (similar interface, different implementation).
21// See https://abseil.io/blog/20171023-cppcon-strcat
22// and https://github.com/abseil/abseil-cpp/blob/master/absl/strings/str_cat.h
23
24
25// --- Public interface ---
26
27// Concatenate a bunch of 'printable' objects.
28//
29// Conceptually each object is converted to a string, all those strings are
30// concatenated, and that result is returned.
31//
32// For example:
33// auto s = strCat("foobar", s, ' ', 123);
34// is equivalent to
35// auto s = "foobar" + s + ' ' + std::to_string(123);
36//
37// The former executes faster than the latter because (among other things) it
38// immediately creates a result of the correct size instead of (re)allocating
39// all intermediate strings. Also it doesn't create temporary string objects
40// for the integer conversions.
41template<typename... Ts>
42[[nodiscard]] std::string strCat(Ts&& ...ts);
43
44// Consider using 'tmpStrCat()' as an alternative for 'strCat()'. The only
45// difference is that this one returns a 'TemporaryString' instead of a
46// 'std::string'. This can be faster (e.g. no heap allocation) when the result
47// is not required to be a 'std::string' (std::string_view is sufficient) and
48// when it really is a temporary (short lived) string.
49template<typename... Ts>
50[[nodiscard]] TemporaryString tmpStrCat(Ts&&... ts);
51
52// Append a bunch of 'printable' objects to an exiting string.
53//
54// Can be used to optimize
55// s += strCat(a, b, c);
56// to
57// strAppend(s, a, b, c);
58//
59// This latter will immediately construct the result in 's' instead of first
60// formatting to a temporary string followed by appending that temporary to 's'.
61//
62// There's one limitation though: it's not allowed to append a string to
63// itself, not even parts of the string (strCat() doesn't have this
64// limitation). So the following is invalid:
65// strAppend(s, s); // INVALID, append s to itself
66//
67// string_view v = s;
68// v.substr(10, 20);
69// strAppend(s, v); // INVALID, append part of s to itself
70template<typename... Ts>
71void strAppend(std::string& result, Ts&& ...ts);
72
73
74// Format an integer as a fixed-width hexadecimal value and insert it into
75// a strCat() or strAppend() sequence.
76// - If the value is small, leading zeros are printed.
77// - If the value is too big, it gets truncated. Only the rightmost characters
78// are kept.
79//
80// For example:
81// s = strCat("The value is: 0x", hex_string<4>(value), '.');
84
85
86// Insert 'n' spaces into a strCat() or strAppend() sequence.
87//
88// For example:
89// s = strCat("The result is ", spaces(30 - item.size()), item);
90//
91// This can be more efficient than creating a temporary string containing 'n'
92// spaces, like this
93// s = strCat("The result is ", std::string(30 - item.size(), ' '), item);
95
96struct Digits {
97 size_t n;
98};
99enum class HexCase {
100 lower, upper
101};
102
103// --- Implementation details ---
104
105namespace strCatImpl {
106
107// ConcatUnit
108// These implement various mechanisms to concatenate an object to a string.
109// All these classes implement:
110//
111// - size_t size() const;
112// Returns the (exact) size in characters of the formatted object.
113//
114// - char* copy(char* dst) const;
115// Copy the formatted object to 'dst', returns an updated pointer.
116template<typename T> struct ConcatUnit;
117
118
119// Helper for types which are formatted via a temporary string object
121{
122 ConcatViaString(std::string s_)
123 : s(std::move(s_))
124 {
125 }
126
127 [[nodiscard]] size_t size() const
128 {
129 return s.size();
130 }
131
132 [[nodiscard]] char* copy(char* dst) const
133 {
134 ranges::copy(s, dst);
135 return dst + s.size();
136 }
137
138private:
139 std::string s;
140};
141
142#if 0
143// Dingux doesn't have std::to_string() ???
144
145// Helper for types which are printed via std::to_string(),
146// e.g. floating point types.
147template<typename T>
148struct ConcatToString : ConcatViaString
149{
150 ConcatToString(T t)
151 : ConcatViaString(std::to_string(t))
152 {
153 }
154};
155#endif
156
157// The default (slow) implementation uses 'operator<<(ostream&, T)'
158template<typename T>
160{
161 ConcatUnit(const T& t)
162 : ConcatViaString([&](){
163 std::ostringstream os;
164 os << t;
165 return os.str();
166 }())
167 {
168 }
169};
170
171
172// ConcatUnit<string_view>:
173// store the string view (copies the view, not the string)
174template<> struct ConcatUnit<std::string_view>
175{
176 ConcatUnit(const std::string_view v_)
177 : v(v_)
178 {
179 }
180
181 [[nodiscard]] size_t size() const
182 {
183 return v.size();
184 }
185
186 [[nodiscard]] char* copy(char* dst) const
187 {
188 ranges::copy(v, dst);
189 return dst + v.size();
190 }
191
192private:
193 std::string_view v;
194};
195
196
197// ConcatUnit<char>:
198// store single char (length is constant 1)
199template<> struct ConcatUnit<char>
200{
201 ConcatUnit(char c_)
202 : c(c_)
203 {
204 }
205
206 [[nodiscard]] size_t size() const
207 {
208 return 1;
209 }
210
211 [[nodiscard]] char* copy(char* dst) const
212 {
213 *dst = c;
214 return dst + 1;
215 }
216
217private:
218 char c;
219};
220
221
222// ConcatUnit<bool>:
223// store bool (length is constant 1)
224template<> struct ConcatUnit<bool>
225{
226 ConcatUnit(bool b_)
227 : b(b_)
228 {
229 }
230
231 [[nodiscard]] size_t size() const
232 {
233 return 1;
234 }
235
236 [[nodiscard]] char* copy(char* dst) const
237 {
238 *dst = b ? '1' : '0';
239 return dst + 1;
240 }
241
242private:
243 bool b;
244};
245
246
247// Helper classes/functions for fast integer -> string conversion.
248
249// Returns a fast type that is wide enough to hold the absolute value for
250// values of the given type.
251//
252// On x86 32-bit operations are faster than e.g. 16-bit operations.
253// So this function returns a 32-bit type unless 64-bit are needed.
254//
255// 'long' is special because it's either 32-bit (windows) or 64-bit (Linux).
256template<typename T> struct FastUnsignedImpl { using type = unsigned; };
257template<> struct FastUnsignedImpl< long> { using type = unsigned long; };
258template<> struct FastUnsignedImpl<unsigned long> { using type = unsigned long; };
259template<> struct FastUnsignedImpl< long long> { using type = unsigned long long; };
260template<> struct FastUnsignedImpl<unsigned long long> { using type = unsigned long long; };
261template<typename T> using FastUnsigned = typename FastUnsignedImpl<T>::type;
262
263// Helper function to take the absolute value of a signed or unsigned type.
264// (without compiler warning on 't < 0' and '-t' when t is unsigned)
265template<std::unsigned_integral T>
266[[nodiscard]] FastUnsigned<T> absHelper(T t) { return t; }
267
268template<std::signed_integral T>
269[[nodiscard]] FastUnsigned<T> absHelper(T t) { return (t < 0)? -t : t; }
270
271
272// Optimized integer printing.
273//
274// Prints the value to an internal buffer. The formatted characters are
275// generated from back to front. This means the final result is not aligned
276// with the start of this internal buffer.
277//
278// Next to the internal buffer we also store the size (in characters) of the
279// result. This size can be used to calculate the start position in the buffer.
280template<std::integral T> struct ConcatIntegral
281{
282 static constexpr bool IS_SIGNED = std::numeric_limits<T>::is_signed;
283 static constexpr size_t BUF_SIZE = 1 + std::numeric_limits<T>::digits10 + IS_SIGNED;
284
286 {
287 auto p = this->end();
288 std::unsigned_integral auto a = absHelper(t);
289
290 do {
291 *--p = static_cast<char>('0' + (a % 10));
292 a /= 10;
293 } while (a);
294
295 if constexpr (IS_SIGNED) {
296 if (t < 0) *--p = '-';
297 }
298 this->sz = static_cast<unsigned char>(this->end() - p);
299 }
300
301 [[nodiscard]] size_t size() const
302 {
303 return sz;
304 }
305
306 [[nodiscard]] char* copy(char* dst) const
307 {
308 ranges::copy(std::span{data(), sz}, dst);
309 return dst + sz;
310 }
311
312 [[nodiscard]] char* copyTail(char* dst, size_t n) const
313 {
314 assert(n <= sz);
315 ranges::copy(std::span{buf.data() + BUF_SIZE - n, n}, dst);
316 return dst + n;
317 }
318
319 [[nodiscard]] operator std::string() const
320 {
321 return std::string(data(), this->size());
322 }
323
324private:
325 [[nodiscard]] const char* data() const { return buf.data() + BUF_SIZE - sz; }
326 [[nodiscard]] auto end() { return buf.end(); }
327
328private:
329 std::array<char, BUF_SIZE> buf;
330 unsigned char sz;
331};
332
333
334// Format an integral as a hexadecimal value with a fixed number of characters.
335// This fixed width means it either adds leading zeros or truncates the result
336// (it keeps the rightmost digits).
337template<HexCase Case, std::integral T> struct ConcatVariableWidthHexIntegral
338{
340 : n(n_.n), t(t_)
341 {
342 }
343
344 [[nodiscard]] size_t size() const
345 {
346 return n;
347 }
348
349 [[nodiscard]] char* copy(char* dst) const
350 {
351 char* p = dst + n;
352 auto u = static_cast<FastUnsigned<T>>(t);
353
354 static constexpr char A = (Case == HexCase::lower) ? 'a' : 'A';
355 repeat(n, [&] {
356 auto d = u & 15;
357 *--p = (d < 10) ? static_cast<char>(d + '0')
358 : static_cast<char>(d - 10 + A);
359 u >>= 4;
360 });
361
362 return dst + n;
363 }
364
365private:
366 size_t n;
367 T t;
368};
369
370// Format an integral as a hexadecimal value with a fixed number of characters.
371// This fixed width means it either adds leading zeros or truncates the result
372// (it keeps the rightmost digits).
373template<size_t N, HexCase Case, std::integral T> struct ConcatFixedWidthHexIntegral
374{
376 : t(t_)
377 {
378 }
379
380 [[nodiscard]] size_t size() const
381 {
382 return N;
383 }
384
385 [[nodiscard]] char* copy(char* dst) const
386 {
387 char* p = dst + N;
388 auto u = static_cast<FastUnsigned<T>>(t);
389
390 static constexpr char A = (Case == HexCase::lower) ? 'a' : 'A';
391 repeat(N, [&] {
392 auto d = u & 15;
393 *--p = (d < 10) ? static_cast<char>(d + '0')
394 : static_cast<char>(d - 10 + A);
395 u >>= 4;
396 });
397
398 return dst + N;
399 }
400
401private:
402 T t;
403};
404
405// Format an integral as a binary value with a fixed number of characters.
406// This fixed width means it either adds leading zeros or truncates the result
407// (it keeps the rightmost digits).
408template<size_t N, std::integral T> struct ConcatFixedWidthBinIntegral
409{
411 : t(t_)
412 {
413 }
414
415 [[nodiscard]] size_t size() const
416 {
417 return N;
418 }
419
420 [[nodiscard]] char* copy(char* dst) const
421 {
422 char* p = dst + N;
423 auto u = static_cast<FastUnsigned<T>>(t);
424
425 repeat(N, [&] {
426 *--p = static_cast<char>((u & 1) + '0');
427 u >>= 1;
428 });
429
430 return dst + N;
431 }
432
433private:
434 T t;
435};
436
437
438// Prints a number of spaces (without constructing a temporary string).
440{
441 ConcatSpaces(size_t n_)
442 : n(n_)
443 {
444 }
445
446 [[nodiscard]] size_t size() const
447 {
448 return n;
449 }
450
451 [[nodiscard]] char* copy(char* dst) const
452 {
453 ranges::fill(std::span{dst, n}, ' ');
454 return dst + n;
455 }
456
457private:
458 size_t n;
459};
460
461
462// Format an integral as a decimal value with a fixed number of characters.
463// This fixed width means it either adds leading spaces or truncates the result
464// (it keeps the rightmost digits).
465template<size_t N, std::integral T> struct ConcatFixedWidthDecIntegral
466{
468 : helper(t)
469 {
470 }
471
472 [[nodiscard]] size_t size() const
473 {
474 return N;
475 }
476
477 [[nodiscard]] char* copy(char* dst) const
478 {
479 auto n2 = helper.size();
480 if (N <= n2) {
481 return helper.copyTail(dst, N);
482 } else {
483 ConcatSpaces spaces(N - n2);
484 auto* p = spaces.copy(dst);
485 return helper.copy(p);
486 }
487 }
488
489private:
490 ConcatIntegral<T> helper;
491};
492
493
494// Create a 'ConcatUnit<T>' wrapper object for a given 'T' value.
495
496// Generic version: use the corresponding ConcatUnit<T> class. This can be
497// a specialized version for 'T', or the generic (slow) version which uses
498// operator<<(ostream&, T).
499template<typename T>
500[[nodiscard]] inline auto makeConcatUnit(const T& t)
501{
502 return ConcatUnit<T>(t);
503}
504
505// Overloads for various cases (strings, integers, floats, ...).
506[[nodiscard]] inline auto makeConcatUnit(const std::string& s)
507{
509}
510
511[[nodiscard]] inline auto makeConcatUnit(const char* s)
512{
514}
515
516[[nodiscard]] inline auto makeConcatUnit(char* s)
517{
519}
520[[nodiscard]] inline auto makeConcatUnit(const TemporaryString& s)
521{
523}
524[[nodiscard]] inline auto makeConcatUnit(zstring_view s)
525{
527}
528
529// Note: no ConcatIntegral<char> because that is printed as a single character
530[[nodiscard]] inline auto makeConcatUnit(signed char c)
531{
533}
534
535[[nodiscard]] inline auto makeConcatUnit(unsigned char c)
536{
538}
539
540[[nodiscard]] inline auto makeConcatUnit(short s)
541{
542 return ConcatIntegral<short>(s);
543}
544
545[[nodiscard]] inline auto makeConcatUnit(unsigned short s)
546{
548}
549
550[[nodiscard]] inline auto makeConcatUnit(int i)
551{
552 return ConcatIntegral<int>(i);
553}
554
555[[nodiscard]] inline auto makeConcatUnit(unsigned u)
556{
557 return ConcatIntegral<unsigned>(u);
558}
559
560[[nodiscard]] inline auto makeConcatUnit(long l)
561{
562 return ConcatIntegral<long>(l);
563}
564
565[[nodiscard]] inline auto makeConcatUnit(unsigned long l)
566{
568}
569
570[[nodiscard]] inline auto makeConcatUnit(long long l)
571{
573}
574
575[[nodiscard]] inline auto makeConcatUnit(unsigned long long l)
576{
578}
579
580#if 0
581// Converting float->string via std::to_string() might be faster than via
582// std::stringstream. Though the former doesn't seem to work on Dingux??
583//
584// But for openMSX this isn't critical, so we can live with the default
585// (slower?) version.
586
587[[nodiscard]] inline auto makeConcatUnit(float f)
588{
589 return ConcatToString<float>(f);
590}
591
592[[nodiscard]] inline auto makeConcatUnit(double d)
593{
594 return ConcatToString<double>(d);
595}
596
597[[nodiscard]] inline auto makeConcatUnit(long double d)
598{
599 return ConcatToString<long double>(d);
600}
601#endif
602
603template<HexCase Case, std::integral T>
605{
606 return t;
607}
608
609template<size_t N, HexCase Case, std::integral T>
611{
612 return t;
613}
614
615template<size_t N, std::integral T>
616[[nodiscard]] inline auto makeConcatUnit(const ConcatFixedWidthBinIntegral<N, T>& t)
617{
618 return t;
619}
620
621template<size_t N, std::integral T>
622[[nodiscard]] inline auto makeConcatUnit(const ConcatFixedWidthDecIntegral<N, T>& t)
623{
624 return t;
625}
626
627[[nodiscard]] inline auto makeConcatUnit(const ConcatSpaces& t)
628{
629 return t;
630}
631
632
633// Calculate the total size for a bunch (a tuple) of ConcatUnit<T> objects.
634// That is, calculate the sum of calling the size() method on each ConcatUnit.
635template<typename Tuple, size_t... Is>
636[[nodiscard]] size_t calcTotalSizeHelper(const Tuple& t, std::index_sequence<Is...>)
637{
638 return (... + std::get<Is>(t).size());
639}
640
641template<typename... Ts>
642[[nodiscard]] size_t calcTotalSize(const std::tuple<Ts...>& t)
643{
644 return calcTotalSizeHelper(t, std::index_sequence_for<Ts...>{});
645}
646
647
648// Copy each ConcatUnit<T> in the given tuple to the final result.
649template<typename Tuple, size_t... Is>
650void copyUnitsHelper(char* dst, const Tuple& t, std::index_sequence<Is...>)
651{
652 auto l = { ((dst = std::get<Is>(t).copy(dst)) , 0)... };
653 (void)l;
654}
655
656template<typename... Ts>
657void copyUnits(char* dst, const std::tuple<Ts...>& t)
658{
659 copyUnitsHelper(dst, t, std::index_sequence_for<Ts...>{});
660}
661
662// Fast integral -> string conversion. (Standalone version, result is not part
663// of a larger string).
664[[nodiscard]] inline std::string to_string(std::integral auto x)
665{
666 return ConcatIntegral(x);
667}
668
669} // namespace strCatImpl
670
671
672// Generic version
673template<typename... Ts>
674[[nodiscard]] std::string strCat(Ts&& ...ts)
675{
676 // Strategy:
677 // - For each parameter (of any type) we create a ConcatUnit object.
678 // - We sum the results of callings size() on all those objects.
679 // - We allocate a string of that total size.
680 // - We copy() each ConcatUnit into that string.
681
682 auto t = std::tuple(strCatImpl::makeConcatUnit(std::forward<Ts>(ts))...);
683 auto size = strCatImpl::calcTotalSize(t);
684 // Ideally we want an uninitialized string with given size, but that's not
685 // yet possible. Though see the following proposal (for c++20):
686 // www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1072r0.html
687 std::string result(size, ' ');
688 char* dst = result.data();
690 return result;
691}
692
693// Optimized strCat() for the case that the 1st parameter is a temporary
694// (rvalue) string. In that case we can append to that temporary.
695template<typename... Ts>
696[[nodiscard]] std::string strCat(std::string&& first, Ts&& ...ts)
697{
698 strAppend(first, std::forward<Ts>(ts)...);
699 return std::move(first);
700}
701
702// Degenerate case
703[[nodiscard]] inline std::string strCat()
704{
705 return {};
706}
707
708// Extra overloads. These don't change and/or extend the above functionality,
709// but in some cases they might improve performance a bit (see notes above
710// about uninitialized string resize). With these overloads strCat()/strAppend()
711// should never be less efficient than a sequence of + or += string-operations.
712[[nodiscard]] inline std::string strCat(const std::string& x) { return x; }
713[[nodiscard]] inline std::string strCat(std::string&& x) { return std::move(x); }
714[[nodiscard]] inline std::string strCat(const char* x) { return {x}; }
715[[nodiscard]] inline std::string strCat(char x) { return std::string(1, x); } // Not: return {1, x};
716[[nodiscard]] inline std::string strCat(std::string_view x) { return {x.data(), x.size()}; }
717
718[[nodiscard]] inline std::string strCat(signed char x) { return strCatImpl::to_string(x); }
719[[nodiscard]] inline std::string strCat(unsigned char x) { return strCatImpl::to_string(x); }
720[[nodiscard]] inline std::string strCat(short x) { return strCatImpl::to_string(x); }
721[[nodiscard]] inline std::string strCat(unsigned short x) { return strCatImpl::to_string(x); }
722[[nodiscard]] inline std::string strCat(int x) { return strCatImpl::to_string(x); }
723[[nodiscard]] inline std::string strCat(unsigned x) { return strCatImpl::to_string(x); }
724[[nodiscard]] inline std::string strCat(long x) { return strCatImpl::to_string(x); }
725[[nodiscard]] inline std::string strCat(unsigned long x) { return strCatImpl::to_string(x); }
726[[nodiscard]] inline std::string strCat(long long x) { return strCatImpl::to_string(x); }
727[[nodiscard]] inline std::string strCat(unsigned long long x) { return strCatImpl::to_string(x); }
728
729[[nodiscard]] inline std::string strCat(const std::string& x, const std::string& y) { return x + y; }
730[[nodiscard]] inline std::string strCat(const char* x, const std::string& y) { return x + y; }
731[[nodiscard]] inline std::string strCat(char x, const std::string& y) { return x + y; }
732[[nodiscard]] inline std::string strCat(const std::string& x, const char* y) { return x + y; }
733[[nodiscard]] inline std::string strCat(const std::string& x, char y) { return x + y; }
734[[nodiscard]] inline std::string strCat(std::string&& x, const std::string& y) { return x + y; }
735[[nodiscard]] inline std::string strCat(const std::string& x, std::string&& y) { return x + y; }
736[[nodiscard]] inline std::string strCat(std::string&& x, std::string&& y) { return x + y; }
737[[nodiscard]] inline std::string strCat(const char* x, std::string&& y) { return x + y; }
738[[nodiscard]] inline std::string strCat(char x, std::string&& y) { return x + y; }
739[[nodiscard]] inline std::string strCat(std::string&& x, const char* y) { return x + y; }
740[[nodiscard]] inline std::string strCat(std::string&& x, char y) { return x + y; }
741
742template<typename... Ts> [[nodiscard]] TemporaryString tmpStrCat(Ts&&... ts)
743{
744 auto t = std::tuple(strCatImpl::makeConcatUnit(std::forward<Ts>(ts))...);
745 auto size = strCatImpl::calcTotalSize(t);
746 return TemporaryString(
747 size, [&](char* dst) { strCatImpl::copyUnits(dst, t); });
748}
749
750// Generic version
751template<typename... Ts>
752void strAppend(std::string& result, Ts&& ...ts)
753{
754 // Implementation strategy is similar to strCat(). Main difference is
755 // that we now extend an existing string instead of creating a new one.
756
757 auto t = std::tuple(strCatImpl::makeConcatUnit(std::forward<Ts>(ts))...);
758 auto extraSize = strCatImpl::calcTotalSize(t);
759 auto oldSize = result.size();
760 result.append(extraSize, ' '); // see note in strCat() about uninitialized string
761 char* dst = &result[oldSize];
763}
764
765// Degenerate case
766inline void strAppend(std::string& /*x*/)
767{
768 // nothing
769}
770
771// Extra overloads, see strCat().
772inline void strAppend(std::string& x, const std::string& y) { x += y; }
773inline void strAppend(std::string& x, const char* y) { x += y; }
774inline void strAppend(std::string& x, std::string_view y) { x.append(y.data(), y.size()); }
775
776
777template<HexCase Case = HexCase::lower, std::integral T>
779{
780 return {n, t};
781}
782
783template<size_t N, HexCase Case = HexCase::lower, std::integral T>
785{
786 return {t};
787}
788
789template<size_t N, std::integral T>
791{
792 return {t};
793}
794
795template<size_t N, std::integral T>
797{
798 return {t};
799}
800
801[[nodiscard]] inline strCatImpl::ConcatSpaces spaces(size_t n)
802{
803 return {n};
804}
805
806#endif
TclObject t
TemporaryString.
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:305
auto copy(InputRange &&range, OutputIter out)
Definition ranges.hh:250
STL namespace.
FastUnsigned< T > absHelper(T t)
Definition strCat.hh:266
auto makeConcatUnit(const T &t)
Definition strCat.hh:500
void copyUnits(char *dst, const std::tuple< Ts... > &t)
Definition strCat.hh:657
void copyUnitsHelper(char *dst, const Tuple &t, std::index_sequence< Is... >)
Definition strCat.hh:650
typename FastUnsignedImpl< T >::type FastUnsigned
Definition strCat.hh:261
size_t calcTotalSizeHelper(const Tuple &t, std::index_sequence< Is... >)
Definition strCat.hh:636
size_t calcTotalSize(const std::tuple< Ts... > &t)
Definition strCat.hh:642
std::string to_string(std::integral auto x)
Definition strCat.hh:664
HexCase
Definition strCat.hh:99
std::string strCat()
Definition strCat.hh:703
strCatImpl::ConcatSpaces spaces(size_t n)
Definition strCat.hh:801
strCatImpl::ConcatVariableWidthHexIntegral< Case, T > hex_string(Digits n, T t)
Definition strCat.hh:778
TemporaryString tmpStrCat(Ts &&... ts)
Definition strCat.hh:742
strCatImpl::ConcatFixedWidthBinIntegral< N, T > bin_string(T t)
Definition strCat.hh:790
void strAppend(std::string &result, Ts &&...ts)
Definition strCat.hh:752
strCatImpl::ConcatFixedWidthDecIntegral< N, T > dec_string(T t)
Definition strCat.hh:796
size_t n
Definition strCat.hh:97
char * copy(char *dst) const
Definition strCat.hh:420
char * copy(char *dst) const
Definition strCat.hh:477
char * copy(char *dst) const
Definition strCat.hh:385
size_t size() const
Definition strCat.hh:301
char * copy(char *dst) const
Definition strCat.hh:306
static constexpr size_t BUF_SIZE
Definition strCat.hh:283
char * copyTail(char *dst, size_t n) const
Definition strCat.hh:312
static constexpr bool IS_SIGNED
Definition strCat.hh:282
char * copy(char *dst) const
Definition strCat.hh:451
ConcatSpaces(size_t n_)
Definition strCat.hh:441
size_t size() const
Definition strCat.hh:446
char * copy(char *dst) const
Definition strCat.hh:236
char * copy(char *dst) const
Definition strCat.hh:211
ConcatUnit(const std::string_view v_)
Definition strCat.hh:176
ConcatUnit(const T &t)
Definition strCat.hh:161
ConcatVariableWidthHexIntegral(Digits n_, T t_)
Definition strCat.hh:339
char * copy(char *dst) const
Definition strCat.hh:132
ConcatViaString(std::string s_)
Definition strCat.hh:122
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition xrange.hh:147