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