Pioneer
Loading...
Searching...
No Matches
StringF.h
Go to the documentation of this file.
1// Copyright © 2008-2024 Pioneer Developers. See AUTHORS.txt for details
2// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3
4#ifndef _STRINGF_H
5#define _STRINGF_H
6
7#include "fixed.h"
8#include "core/macros.h"
9
10#include <string>
11
12// provides (for integer types, floating point types, const char* and std::string):
13//
14// Basic value -> string functions:
15// std::string to_string(T value);
16// std::string to_string(T value, const FormatSpec& format);
17//
18// You may extend the system by providing your own overloads for
19// std::string to_string(T value, const FormatSpec& format)
20//
21// String formatter:
22// std::string stringf(const char* fmt, ...);
23//
24// This should work for up to 7 arguments, where each argument is:
25// - An object of type FormatArg or FormatArgT<T> for some T
26// - Or, the result of a call to formatarg(name, value)
27// - Or, a value of a type that can be converted to a string with to_string
28//
29// formatarg() allows you to give a name and optionally a default format
30// for an argument to stringf()
31// e.g., formatarg("distance", 42.5, "f.2")
32//
33// That argument can then be referenced in the format template as %distance,
34// and will be formatted as a fixed-point number with 2 decimal places.
35//
36// stringf(), along with FormatArg and formatarg() is a wrapper around
37// string_format(const char* fmt, int numargs, const FormatArg * const args[])
38//
39// Syntax for argument references:
40// ref = '%' ( ident | int | '{' [^}]+ '}' ) ( '{' formatspec '}' )?
41// int = [0-9]+
42// ident = [a-zA-Z_] [a-zA-Z0-9_]*
43// alpha = [a-zA-Z]
44// formatspec = alpha+ ( ':'? fmtparam ( '|' fmtparam)* )
45// fmtparam = ( [^}\] | '\' any )*
46//
47// To insert a literal % character, use %% (as in printf)
48//
49// References are either an integer argument index (0-based),
50// or a text string, which can follow C identifier rules, or be any string
51// enclosed in braces.
52//
53// The format specifier, if provided, consists of a style name, followed by
54// a series of parameters. Style names are alphabetic only (no underscore,
55// digits or puncutation). Parameters may immediately follow the style name,
56// or be separated from the style name by a colon.
57// Parameters are separated from each other by a pipe character.
58// Backslash may be used within format parameters to escape '|' and '}',
59// more generally, backslash within a format parameter causes the next
60// character to be taken as a literal, regardless of what that character is
61//
62// Examples of references:
63// stringf("Hello, %0.", "Jameson") -> "Hello, Jameson."
64// stringf("Hello, %0.", formatarg("name", "Jameson")) -> "Hello, Jameson."
65// stringf("Hello, %name.", formatarg("name", "Jameson")) -> "Hello, Jameson."
66// stringf("That's %{mood}tastic!", formatarg("mood", "funky")) -> "That's funkytastic!"
67//
68// stringf("I've already wasted %count %{trip(s)} on this fooling endeavour!",
69// formatarg("count", 3), formatarg("trip(s)", "trips"))
70// -> "I've already wasted 3 trips on this fooling endeavour!"
71//
72// stringf("I've already wasted %count %{trip(s)} on this fooling endeavour!",
73// formatarg("count", 1), formatarg("trip(s)", "trip"))
74// -> "I've already wasted 1 trip on this fooling endeavour!"
75//
76// stringf("That'll be %0 credits, Mr. %1.", 50, "Jameson")
77// -> "That'll be 50 credits, Mr. Jameson."
78// stringf("Excellent choice, Mr. %1! That'll be %0 credits, please.", 50, "Jameson")
79// -> "Excellent choice, Mr. Jameson! That'll be 50 credits, please."
80//
81// Currently implemented format styles are designed to mostly match printf()
82// specifiers, except to follow the general syntax described above, the
83// specifier itself comes first, then any flags as a parameter. Only numeric
84// types currently interpret these format specifiers. So:
85//
86// printf("%s", "Hello") =~= stringf("%0", "Hello")
87// printf("%f", 42.125) =~= stringf("%0{f}", 42.125)
88// printf("%.2f", 42.125) =~= stringf("%0{f.2}", 42.125)
89// printf("%+2.3f", 42.125) =~= stringf("%0{f+2.3}", 42.125)
90// printf("%08d", 42) =~= stringf("%0{d08}", 42)
91//
92
94public:
95 FormatSpec();
96 FormatSpec(const char *format);
97 FormatSpec(const char *format, int formatlen);
98
99 bool empty() const;
100
101 // access to components of the formatspec
102 bool specifierIs(const char *specifier) const;
103 int paramCount() const;
104 std::string param(int idx) const;
105 void paramPtr(int idx, const char *&begin, const char *&end) const;
106
107private:
108 static const int MAX_PARAMS = 3;
109
110 void parseFormat(int length);
111
112 const char *const format;
113 // each entry in the params array specifies the index within format[]
114 // of the first byte in the parameter
115 uint16_t params[MAX_PARAMS + 1];
116};
117
118std::string to_string(int8_t value, const FormatSpec &fmt);
119std::string to_string(int16_t value, const FormatSpec &fmt);
120std::string to_string(int32_t value, const FormatSpec &fmt);
121std::string to_string(int64_t value, const FormatSpec &fmt);
122std::string to_string(uint8_t value, const FormatSpec &fmt);
123std::string to_string(uint16_t value, const FormatSpec &fmt);
124std::string to_string(uint32_t value, const FormatSpec &fmt);
125std::string to_string(uint64_t value, const FormatSpec &fmt);
126std::string to_string(float value, const FormatSpec &fmt);
127std::string to_string(double value, const FormatSpec &fmt);
128std::string to_string(fixed value, const FormatSpec &fmt);
129std::string to_string(const char *value, const FormatSpec &fmt);
130std::string to_string(const std::string &value, const FormatSpec &fmt);
131
132inline std::string to_string(int8_t value, const FormatSpec &fmt) { return to_string(int64_t(value), fmt); }
133inline std::string to_string(int16_t value, const FormatSpec &fmt) { return to_string(int64_t(value), fmt); }
134inline std::string to_string(int32_t value, const FormatSpec &fmt) { return to_string(int64_t(value), fmt); }
135inline std::string to_string(uint8_t value, const FormatSpec &fmt) { return to_string(uint64_t(value), fmt); }
136inline std::string to_string(uint16_t value, const FormatSpec &fmt) { return to_string(uint64_t(value), fmt); }
137inline std::string to_string(uint32_t value, const FormatSpec &fmt) { return to_string(uint64_t(value), fmt); }
138
139inline std::string to_string(float value, const FormatSpec &fmt) { return to_string(double(value), fmt); }
140
141inline std::string to_string(fixed value, const FormatSpec &fmt)
142{
143 return to_string(value.ToDouble(), fmt);
144}
145
146template <typename T>
147inline std::string to_string(const T &value)
148{
149 return to_string(value, FormatSpec());
150}
151
153public:
154 explicit FormatArg(const char *name_ = 0, const char *defaultformat_ = 0) :
155 name(name_),
156 defaultformat(defaultformat_) {}
157
158 char const *const name;
159 char const *const defaultformat;
160
161 virtual std::string format(const FormatSpec &spec) const = 0;
162};
163
164template <typename T>
165class FormatArgT : public FormatArg {
166public:
167 FormatArgT(const char *name_, const T &value_, const char *defaultformat_) :
168 FormatArg(name_, defaultformat_),
169 value(value_) {}
170
171 virtual std::string format(const FormatSpec &spec) const
172 {
173 return to_string(value, spec);
174 }
175
176private:
177 const T value;
178};
179
180// ---------------------------------------------------------------------------
181
182template <typename T>
183struct FormatArgWrapper;
184
185template <typename T>
188 static type wrap(const T &arg, const char *name = 0, const char *defaultformat = 0)
189 {
190 return FormatArgT<T>(name, arg, defaultformat);
191 }
192};
193template <int N>
194struct FormatArgWrapper<char[N]> {
196 static type wrap(const char (&arg)[N], const char *name = 0, const char *defaultformat = 0)
197 {
198 return FormatArgT<const char *>(name, arg, defaultformat);
199 }
200};
201template <>
202struct FormatArgWrapper<char[]> {
204 static type wrap(const char *arg, const char *name = 0, const char *defaultformat = 0)
205 {
206 return FormatArgT<const char *>(name, arg, defaultformat);
207 }
208};
209template <>
212 static const type &wrap(const FormatArg &arg) { return arg; }
213};
214template <typename T>
217 static const type &wrap(const FormatArgT<T> &arg) { return arg; }
218};
219
220// ---------------------------------------------------------------------------
221
222// this version is safer (doesn't rely on the value out-living the FormatArgT object)
223// but performs a string copy
224/*
225FormatArgT<std::string> formatarg(const char* name, const char* value) {
226 return FormatArgT<std::string>(name, std::string(value));
227}
228*/
229
230template <typename T>
231inline typename FormatArgWrapper<T>::type
232formatarg(const char *name, const T &value, const char *defaultformat = 0)
233{
234 return FormatArgWrapper<T>::wrap(value, name, defaultformat);
235}
236
237// underlying formatting function
238
239std::string string_format(const char *fmt, int numargs, FormatArg const *const args[]);
240
241// ---------------------------------------------------------------------------
242
243// ---- stringf(format, args...) for 0 to 7 arguments ----
244
245inline std::string stringf(const char *fmt)
246{
247 return string_format(fmt, 0, 0);
248}
249
250template <typename T0>
251inline std::string stringf(const char *fmt, const T0 &p0)
252{
254 FormatArg const *const args[] = { &arg0 };
255 return string_format(fmt, COUNTOF(args), args);
256}
257
258template <typename T0, typename T1>
259inline std::string stringf(const char *fmt, const T0 &p0, const T1 &p1)
260{
263 FormatArg const *const args[] = { &arg0, &arg1 };
264 return string_format(fmt, COUNTOF(args), args);
265}
266
267template <typename T0, typename T1, typename T2>
268inline std::string stringf(const char *fmt, const T0 &p0, const T1 &p1, const T2 &p2)
269{
273 FormatArg const *const args[] = { &arg0, &arg1, &arg2 };
274 return string_format(fmt, COUNTOF(args), args);
275}
276
277template <typename T0, typename T1, typename T2, typename T3>
278inline std::string stringf(const char *fmt, const T0 &p0, const T1 &p1, const T2 &p2, const T3 &p3)
279{
284 FormatArg const *const args[] = { &arg0, &arg1, &arg2, &arg3 };
285 return string_format(fmt, COUNTOF(args), args);
286}
287
288template <typename T0, typename T1, typename T2, typename T3, typename T4>
289inline std::string stringf(const char *fmt, const T0 &p0, const T1 &p1, const T2 &p2, const T3 &p3, const T4 &p4)
290{
296 FormatArg const *const args[] = { &arg0, &arg1, &arg2, &arg3, &arg4 };
297 return string_format(fmt, COUNTOF(args), args);
298}
299
300template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
301inline std::string stringf(const char *fmt, const T0 &p0, const T1 &p1, const T2 &p2, const T3 &p3, const T4 &p4, const T5 &p5)
302{
309 FormatArg const *const args[] = { &arg0, &arg1, &arg2, &arg3, &arg4, &arg5 };
310 return string_format(fmt, COUNTOF(args), args);
311}
312
313template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
314inline 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)
315{
323 FormatArg const *const args[] = { &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6 };
324 return string_format(fmt, COUNTOF(args), args);
325}
326
327#endif
FormatArgWrapper< T >::type formatarg(const char *name, const T &value, const char *defaultformat=0)
Definition: StringF.h:232
std::string stringf(const char *fmt)
Definition: StringF.h:245
std::string to_string(int8_t value, const FormatSpec &fmt)
Definition: StringF.h:132
std::string string_format(const char *fmt, int numargs, FormatArg const *const args[])
Definition: StringF.cpp:435
Definition: StringF.h:165
virtual std::string format(const FormatSpec &spec) const
Definition: StringF.h:171
FormatArgT(const char *name_, const T &value_, const char *defaultformat_)
Definition: StringF.h:167
Definition: StringF.h:152
virtual std::string format(const FormatSpec &spec) const =0
char const *const defaultformat
Definition: StringF.h:159
char const *const name
Definition: StringF.h:158
FormatArg(const char *name_=0, const char *defaultformat_=0)
Definition: StringF.h:154
Definition: StringF.h:93
bool empty() const
Definition: StringF.cpp:37
FormatSpec()
Definition: StringF.cpp:16
std::string param(int idx) const
Definition: StringF.cpp:58
int paramCount() const
Definition: StringF.cpp:50
void paramPtr(int idx, const char *&begin, const char *&end) const
Definition: StringF.cpp:89
bool specifierIs(const char *specifier) const
Definition: StringF.cpp:42
double ToDouble() const
Definition: fixed.h:192
#define COUNTOF(array)
Definition: macros.h:39
FormatArgT< T > type
Definition: StringF.h:216
static const type & wrap(const FormatArgT< T > &arg)
Definition: StringF.h:217
FormatArg type
Definition: StringF.h:211
static const type & wrap(const FormatArg &arg)
Definition: StringF.h:212
static type wrap(const char(&arg)[N], const char *name=0, const char *defaultformat=0)
Definition: StringF.h:196
FormatArgT< const char * > type
Definition: StringF.h:195
static type wrap(const char *arg, const char *name=0, const char *defaultformat=0)
Definition: StringF.h:204
FormatArgT< const char * > type
Definition: StringF.h:203
Definition: StringF.h:186
static type wrap(const T &arg, const char *name=0, const char *defaultformat=0)
Definition: StringF.h:188
FormatArgT< T > type
Definition: StringF.h:187