1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
/*
* Typesafe printf-like wrapper around std streams
* by Igor Kozyrenko, 2013
*/
#include "utils/safeformat.h"
#include <sstream>
#include <algorithm>
#include <iterator>
namespace typesafe_format {
namespace impl {
const char kPlaceholderPrefix = '{';
const char kPlaceholderSuffix = '}';
struct Placeholder {
bool escaped;
size_t escaped_prefixes;
size_t number;
size_t length;
};
void missing_parameter(std::ostream& os, size_t param_number) {
os<<"{Parameter "<<param_number<<" is missing}";
}
void missing_parameter(std::wostream& os, size_t param_number) {
os<<L"{Parameter "<<param_number<<L" is missing}";
}
template <typename CT>
const CT* find_placeholder_tmpl(const CT* text, const CT* end,
Placeholder& ph) {
const CT* prefix_begin = text;
bool done = false;
do {
prefix_begin = std::find(prefix_begin, end, kPlaceholderPrefix);
// there must be at least two symbols after prefix
if (std::distance(prefix_begin, end) > 2) {
const CT* prefix_end = prefix_begin + 1;
// count prefix escapes if any
while(*prefix_end == kPlaceholderPrefix &&
std::distance(prefix_end, end) > 2)
++prefix_end;
size_t prefixes = std::distance(prefix_begin, prefix_end);
size_t prefix_number = *prefix_end - '0';
const CT* suffix = prefix_end + 1;
if (prefix_number < 10 && // it is in range of valid placeholders
*suffix == kPlaceholderSuffix) {
ph.escaped = prefixes % 2 == 0;
ph.escaped_prefixes = prefixes / 2;
ph.number = prefix_number;
ph.length = (suffix - prefix_begin) + 1;
done = true;
} else {
prefix_begin = suffix; // continue from the symbol after number
}
} else { // prefix_begin is not at least last but two
prefix_begin = end;
done = true;
}
} while (!done);
return prefix_begin;
}
template <typename CT>
void format_params_tmpl(const CT* fmt, size_t fmt_length,
std::basic_ostream<CT>& os,
const Streamable<CT>* params[], size_t params_count) {
const CT* text = fmt;
const CT* end = fmt + fmt_length;
Placeholder placeholder;
for (const CT* ph_pos = find_placeholder_tmpl(text, end, placeholder);
ph_pos != end;
ph_pos = find_placeholder_tmpl(text, end, placeholder)) {
os.write(text, ph_pos - text);
for (size_t i = 0; i != placeholder.escaped_prefixes; ++i)
os.put(kPlaceholderPrefix);
if (placeholder.escaped) {
os<<placeholder.number<<kPlaceholderSuffix;
} else {
if (placeholder.number < params_count)
params[placeholder.number]->OutputToStream(os);
else {
missing_parameter(os, placeholder.number);
}
}
text = ph_pos + placeholder.length;
}
os.write(text, end - text);
}
void format_params(const char* fmt, size_t fmt_length, std::ostream& os,
const Streamable<char>* params[], size_t params_count) {
format_params_tmpl(fmt, fmt_length, os, params, params_count);
}
void format_params(const wchar_t* fmt, size_t fmt_length, std::wostream& os,
const Streamable<wchar_t>* params[], size_t params_count) {
format_params_tmpl(fmt, fmt_length, os, params, params_count);
}
} // namespace impl
} // namespace typesafe_format
|