forked from pioneerspacesim/pioneer
/
StringF.h
296 lines (257 loc) · 12.2 KB
/
StringF.h
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#ifndef _STRINGF_H
#define _STRINGF_H
#include "libs.h"
#include <string>
#include <SDL_stdinc.h>
// provides (for integer types, floating point types, const char* and std::string):
//
// Basic value -> string functions:
// std::string to_string(T value);
// std::string to_string(T value, const FormatSpec& format);
//
// You may extend the system by providing your own overloads for
// std::string to_string(T value, const FormatSpec& format)
//
// String formatter:
// std::string stringf(const char* fmt, ...);
//
// This should work for up to 7 arguments, where each argument is:
// - An object of type FormatArg or FormatArgT<T> for some T
// - Or, the result of a call to formatarg(name, value)
// - Or, a value of a type that can be converted to a string with to_string
//
// formatarg() allows you to give a name and optionally a default format
// for an argument to stringf()
// e.g., formatarg("distance", 42.5, "f.2")
//
// That argument can then be referenced in the format template as %distance,
// and will be formatted as a fixed-point number with 2 decimal places.
//
// stringf(), along with FormatArg and formatarg() is a wrapper around
// string_format(const char* fmt, int numargs, const FormatArg * const args[])
//
// Syntax for argument references:
// ref = '%' ( ident | int | '{' [^}]+ '}' ) ( '{' formatspec '}' )?
// int = [0-9]+
// ident = [a-zA-Z_] [a-zA-Z0-9_]*
// alpha = [a-zA-Z]
// formatspec = alpha+ ( ':'? fmtparam ( '|' fmtparam)* )
// fmtparam = ( [^}\] | '\' any )*
//
// To insert a literal % character, use %% (as in printf)
//
// References are either an integer argument index (0-based),
// or a text string, which can follow C identifier rules, or be any string
// enclosed in braces.
//
// The format specifier, if provided, consists of a style name, followed by
// a series of parameters. Style names are alphabetic only (no underscore,
// digits or puncutation). Parameters may immediately follow the style name,
// or be separated from the style name by a colon.
// Parameters are separated from each other by a pipe character.
// Backslash may be used within format parameters to escape '|' and '}',
// more generally, backslash within a format parameter causes the next
// character to be taken as a literal, regardless of what that character is
//
// Examples of references:
// stringf("Hello, %0.", "Jameson") -> "Hello, Jameson."
// stringf("Hello, %0.", formatarg("name", "Jameson")) -> "Hello, Jameson."
// stringf("Hello, %name.", formatarg("name", "Jameson")) -> "Hello, Jameson."
// stringf("That's %{mood}tastic!", formatarg("mood", "funky")) -> "That's funkytastic!"
//
// stringf("I've already wasted %count %{trip(s)} on this fooling endeavour!",
// formatarg("count", 3), formatarg("trip(s)", "trips"))
// -> "I've already wasted 3 trips on this fooling endeavour!"
//
// stringf("I've already wasted %count %{trip(s)} on this fooling endeavour!",
// formatarg("count", 1), formatarg("trip(s)", "trip"))
// -> "I've already wasted 1 trip on this fooling endeavour!"
//
// stringf("That'll be %0 credits, Mr. %1.", 50, "Jameson")
// -> "That'll be 50 credits, Mr. Jameson."
// stringf("Excellent choice, Mr. %1! That'll be %0 credits, please.", 50, "Jameson")
// -> "Excellent choice, Mr. Jameson! That'll be 50 credits, please."
//
// Currently implemented format styles are designed to mostly match printf()
// specifiers, except to follow the general syntax described above, the
// specifier itself comes first, then any flags as a parameter. Only numeric
// types currently interpret these format specifiers. So:
//
// printf("%s", "Hello") =~= stringf("%0", "Hello")
// printf("%f", 42.125) =~= stringf("%0{f}", 42.125)
// printf("%.2f", 42.125) =~= stringf("%0{f.2}", 42.125)
// printf("%+2.3f", 42.125) =~= stringf("%0{f+2.3}", 42.125)
// printf("%08d", 42) =~= stringf("%0{d08}", 42)
//
class FormatSpec {
public:
FormatSpec();
FormatSpec(const char* format);
FormatSpec(const char* format, int formatlen);
bool empty() const;
// access to components of the formatspec
bool specifierIs(const char* specifier) const;
int paramCount() const;
std::string param(int idx) const;
void paramPtr(int idx, const char*& begin, const char*& end) const;
private:
static const int MAX_PARAMS = 3;
void parseFormat(int length);
const char * const format;
// each entry in the params array specifies the index within format[]
// of the first byte in the parameter
uint16_t params[MAX_PARAMS+1];
};
std::string to_string(int32_t value, const FormatSpec& fmt);
std::string to_string(int64_t value, const FormatSpec& fmt);
std::string to_string(uint32_t value, const FormatSpec& fmt);
std::string to_string(uint64_t value, const FormatSpec& fmt);
std::string to_string(float value, const FormatSpec& fmt);
std::string to_string(double value, const FormatSpec& fmt);
std::string to_string(fixed value, const FormatSpec& fmt);
std::string to_string(const char* value, const FormatSpec& fmt);
std::string to_string(const std::string& value, const FormatSpec& fmt);
inline std::string to_string(int32_t value, const FormatSpec& fmt) {
return to_string(int64_t(value), fmt);
}
inline std::string to_string(uint32_t value, const FormatSpec& fmt) {
return to_string(uint64_t(value), fmt);
}
inline std::string to_string(float value, const FormatSpec& fmt) {
return to_string(double(value), fmt);
}
inline std::string to_string(fixed value, const FormatSpec& fmt) {
return to_string(value.ToDouble(), fmt);
}
template <typename T>
inline std::string to_string(const T& value) {
return to_string(value, FormatSpec());
}
class FormatArg {
public:
explicit FormatArg(const char* name_ = 0, const char* defaultformat_ = 0):
name(name_), defaultformat(defaultformat_) {}
char const * const name;
char const * const defaultformat;
virtual std::string format(const FormatSpec& spec) const = 0;
};
template <typename T>
class FormatArgT : public FormatArg {
public:
FormatArgT(const char* name_, const T& value_, const char* defaultformat_):
FormatArg(name_, defaultformat_), value(value_) {}
virtual std::string format(const FormatSpec& spec) const {
return to_string(value, spec);
}
private:
const T value;
};
// ---------------------------------------------------------------------------
template <typename T> struct FormatArgWrapper;
template <typename T> struct FormatArgWrapper {
typedef FormatArgT<T> type;
static type wrap(const T& arg, const char* name = 0, const char* defaultformat = 0)
{ return FormatArgT<T>(name, arg, defaultformat); }
};
template <int N> struct FormatArgWrapper<char[N]> {
typedef FormatArgT<const char*> type;
static type wrap(const char (&arg)[N], const char* name = 0, const char* defaultformat = 0)
{ return FormatArgT<const char*>(name, arg, defaultformat); }
};
template <> struct FormatArgWrapper<char[]> {
typedef FormatArgT<const char*> type;
static type wrap(const char *arg, const char* name = 0, const char* defaultformat = 0)
{ return FormatArgT<const char*>(name, arg, defaultformat); }
};
template <> struct FormatArgWrapper<FormatArg> {
typedef FormatArg type;
static const type& wrap(const FormatArg& arg) { return arg; }
};
template <typename T> struct FormatArgWrapper< FormatArgT<T> > {
typedef FormatArgT<T> type;
static const type& wrap(const FormatArgT<T>& arg) { return arg; }
};
// ---------------------------------------------------------------------------
// this version is safer (doesn't rely on the value out-living the FormatArgT object)
// but performs a string copy
/*
FormatArgT<std::string> formatarg(const char* name, const char* value) {
return FormatArgT<std::string>(name, std::string(value));
}
*/
template <typename T>
inline typename FormatArgWrapper<T>::type
formatarg(const char* name, const T& value, const char* defaultformat = 0) {
return FormatArgWrapper<T>::wrap(value, name, defaultformat);
}
// underlying formatting function
std::string string_format(const char* fmt, int numargs, FormatArg const * const args[]);
// ---------------------------------------------------------------------------
// ---- stringf(format, args...) for 0 to 7 arguments ----
inline std::string stringf(const char* fmt) {
return string_format(fmt, 0, 0);
}
template <typename T0>
inline std::string stringf(const char* fmt, const T0& p0) {
const typename FormatArgWrapper<T0>::type& arg0 = FormatArgWrapper<T0>::wrap(p0);
FormatArg const * const args[] = { &arg0 };
return string_format(fmt, 1, args);
}
template <typename T0, typename T1>
inline std::string stringf(const char* fmt, const T0& p0, const T1& p1) {
const typename FormatArgWrapper<T0>::type& arg0 = FormatArgWrapper<T0>::wrap(p0);
const typename FormatArgWrapper<T1>::type& arg1 = FormatArgWrapper<T1>::wrap(p1);
FormatArg const * const args[] = { &arg0, &arg1 };
return string_format(fmt, 2, args);
}
template <typename T0, typename T1, typename T2>
inline std::string stringf(const char* fmt, const T0& p0, const T1& p1, const T2& p2) {
const typename FormatArgWrapper<T0>::type& arg0 = FormatArgWrapper<T0>::wrap(p0);
const typename FormatArgWrapper<T1>::type& arg1 = FormatArgWrapper<T1>::wrap(p1);
const typename FormatArgWrapper<T2>::type& arg2 = FormatArgWrapper<T2>::wrap(p2);
FormatArg const * const args[] = { &arg0, &arg1, &arg2 };
return string_format(fmt, 3, args);
}
template <typename T0, typename T1, typename T2, typename T3>
inline std::string stringf(const char* fmt, const T0& p0, const T1& p1, const T2& p2, const T3& p3) {
const typename FormatArgWrapper<T0>::type& arg0 = FormatArgWrapper<T0>::wrap(p0);
const typename FormatArgWrapper<T1>::type& arg1 = FormatArgWrapper<T1>::wrap(p1);
const typename FormatArgWrapper<T2>::type& arg2 = FormatArgWrapper<T2>::wrap(p2);
const typename FormatArgWrapper<T3>::type& arg3 = FormatArgWrapper<T3>::wrap(p3);
FormatArg const * const args[] = { &arg0, &arg1, &arg2, &arg3 };
return string_format(fmt, 4, args);
}
template <typename T0, typename T1, typename T2, typename T3, typename T4>
inline std::string stringf(const char* fmt, const T0& p0, const T1& p1, const T2& p2, const T3& p3, const T4& p4) {
const typename FormatArgWrapper<T0>::type& arg0 = FormatArgWrapper<T0>::wrap(p0);
const typename FormatArgWrapper<T1>::type& arg1 = FormatArgWrapper<T1>::wrap(p1);
const typename FormatArgWrapper<T2>::type& arg2 = FormatArgWrapper<T2>::wrap(p2);
const typename FormatArgWrapper<T3>::type& arg3 = FormatArgWrapper<T3>::wrap(p3);
const typename FormatArgWrapper<T4>::type& arg4 = FormatArgWrapper<T4>::wrap(p4);
FormatArg const * const args[] = { &arg0, &arg1, &arg2, &arg3, &arg4 };
return string_format(fmt, 5, args);
}
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
inline std::string stringf(const char* fmt, const T0& p0, const T1& p1, const T2& p2, const T3& p3, const T4& p4, const T5& p5) {
const typename FormatArgWrapper<T0>::type& arg0 = FormatArgWrapper<T0>::wrap(p0);
const typename FormatArgWrapper<T1>::type& arg1 = FormatArgWrapper<T1>::wrap(p1);
const typename FormatArgWrapper<T2>::type& arg2 = FormatArgWrapper<T2>::wrap(p2);
const typename FormatArgWrapper<T3>::type& arg3 = FormatArgWrapper<T3>::wrap(p3);
const typename FormatArgWrapper<T4>::type& arg4 = FormatArgWrapper<T4>::wrap(p4);
const typename FormatArgWrapper<T5>::type& arg5 = FormatArgWrapper<T5>::wrap(p5);
FormatArg const * const args[] = { &arg0, &arg1, &arg2, &arg3, &arg4, &arg5 };
return string_format(fmt, 6, args);
}
template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
inline std::string stringf(const char* fmt, const T0& p0, const T1& p1, const T2& p2, const T3& p3, const T4& p4, const T5& p5, const T6& p6) {
const typename FormatArgWrapper<T0>::type& arg0 = FormatArgWrapper<T0>::wrap(p0);
const typename FormatArgWrapper<T1>::type& arg1 = FormatArgWrapper<T1>::wrap(p1);
const typename FormatArgWrapper<T2>::type& arg2 = FormatArgWrapper<T2>::wrap(p2);
const typename FormatArgWrapper<T3>::type& arg3 = FormatArgWrapper<T3>::wrap(p3);
const typename FormatArgWrapper<T4>::type& arg4 = FormatArgWrapper<T4>::wrap(p4);
const typename FormatArgWrapper<T5>::type& arg5 = FormatArgWrapper<T5>::wrap(p5);
const typename FormatArgWrapper<T6>::type& arg6 = FormatArgWrapper<T6>::wrap(p6);
FormatArg const * const args[] = { &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6 };
return string_format(fmt, 7, args);
}
#endif