1 /* Defines String::compose(fmt, arg...) for easy, i18n-friendly
2 * composition of strings.
3 *
4 * Version 1.0.
5 *
6 * Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 //
24 // Basic usage is like
25 //
26 // std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
27 //
28 // See http://www.cs.auc.dk/~olau/compose/ or the included README.compose for
29 // more details.
30 //
31
32 #ifndef STRING_COMPOSE_H
33 #define STRING_COMPOSE_H
34
35 #include <sstream>
36 #include <string>
37 #include <list>
38 #include <map> // for multimap
39
40 #include "pbd/libpbd_visibility.h"
41
42 namespace StringPrivate
43 {
44 // the actual composition class - using string::compose is cleaner, so we
45 // hide it here
46 class LIBPBD_API Composition
47 {
48 public:
49 // initialize and prepare format string on the form "text %1 text %2 etc."
50 explicit Composition(std::string fmt);
51
52 // supply an replacement argument starting from %1
53 template <typename T>
54 Composition &arg(const T &obj);
55
56 // specialization to catch strings (C++ and C)
57 Composition &arg(const std::string &str);
58 Composition &arg(char const * const cstr);
59
60 // compose and return string
61 std::string str() const;
62
63 private:
64 std::ostringstream os;
65 int arg_no;
66
67 // we store the output as a list - when the output string is requested, the
68 // list is concatenated to a string; this way we can keep iterators into
69 // the list instead of into a string where they're possibly invalidated on
70 // inserting a specification string
71 typedef std::list<std::string> output_list;
72 output_list output;
73
74 // the initial parse of the format string fills in the specification map
75 // with positions for each of the various %?s
76 typedef std::multimap<int, output_list::iterator> specification_map;
77 specification_map specs;
78 };
79
80 // helper for converting spec string numbers
char_to_int(char c)81 inline int char_to_int(char c)
82 {
83 switch (c) {
84 case '0': return 0;
85 case '1': return 1;
86 case '2': return 2;
87 case '3': return 3;
88 case '4': return 4;
89 case '5': return 5;
90 case '6': return 6;
91 case '7': return 7;
92 case '8': return 8;
93 case '9': return 9;
94 default: return -1000;
95 }
96 }
97
is_number(int n)98 inline bool is_number(int n)
99 {
100 switch (n) {
101 case '0':
102 case '1':
103 case '2':
104 case '3':
105 case '4':
106 case '5':
107 case '6':
108 case '7':
109 case '8':
110 case '9':
111 return true;
112
113 default:
114 return false;
115 }
116 }
117
118 // implementation of class Composition
119 template <typename T>
arg(const T & obj)120 inline Composition &Composition::arg(const T &obj)
121 {
122 os << obj;
123
124 std::string rep = os.str();
125
126 if (!rep.empty()) { // manipulators don't produce output
127 for (specification_map::const_iterator i = specs.lower_bound(arg_no),
128 end = specs.upper_bound(arg_no); i != end; ++i) {
129 output_list::iterator pos = i->second;
130 ++pos;
131
132 output.insert(pos, rep);
133 }
134
135 os.str(std::string());
136 //os.clear();
137 ++arg_no;
138 }
139
140 return *this;
141 }
142
arg(const std::string & str)143 inline Composition &Composition::arg(const std::string &str)
144 {
145 /* specialization to ensure that empty strings show up
146 * in the output
147 */
148 for (specification_map::const_iterator i = specs.lower_bound(arg_no),
149 end = specs.upper_bound(arg_no); i != end; ++i) {
150 output_list::iterator pos = i->second;
151 ++pos;
152
153 output.insert(pos, str);
154 }
155
156 ++arg_no;
157
158 return *this;
159 }
160
arg(char const * const cstr)161 inline Composition &Composition::arg(char const * const cstr)
162 {
163 /* specialization to ensure that empty C strings show up
164 * in the output
165 */
166 for (specification_map::const_iterator i = specs.lower_bound(arg_no),
167 end = specs.upper_bound(arg_no); i != end; ++i) {
168 output_list::iterator pos = i->second;
169 ++pos;
170
171 output.insert(pos, std::string (cstr));
172 }
173
174 ++arg_no;
175
176 return *this;
177 }
178
Composition(std::string fmt)179 inline Composition::Composition(std::string fmt)
180 : arg_no(1)
181 {
182 std::string::size_type b = 0, i = 0;
183
184 // fill in output with the strings between the %1 %2 %3 etc. and
185 // fill in specs with the positions
186 while (i < fmt.length()) {
187 if (fmt[i] == '%' && i + 1 < fmt.length()) {
188 if (fmt[i + 1] == '%') { // catch %%
189 fmt.replace(i, 2, "%");
190 ++i;
191 }
192 else if (is_number(fmt[i + 1])) { // aha! a spec!
193 // save string
194 output.push_back(fmt.substr(b, i - b));
195
196 int n = 1; // number of digits
197 int spec_no = 0;
198
199 do {
200 spec_no += char_to_int(fmt[i + n]);
201 spec_no *= 10;
202 ++n;
203 } while (i + n < fmt.length() && is_number(fmt[i + n]));
204
205 spec_no /= 10;
206 output_list::iterator pos = output.end();
207 --pos; // safe since we have just inserted a string>
208
209 specs.insert(specification_map::value_type(spec_no, pos));
210
211 // jump over spec string
212 i += n;
213 b = i;
214 }
215 else
216 ++i;
217 }
218 else
219 ++i;
220 }
221
222 if (i - b > 0) { // add the rest of the string
223 output.push_back(fmt.substr(b, i - b));
224 }
225 }
226
str()227 inline std::string Composition::str() const
228 {
229 // assemble string
230 std::string str;
231
232 for (output_list::const_iterator i = output.begin(), end = output.end();
233 i != end; ++i)
234 str += *i;
235
236 return str;
237 }
238 }
239
240 // now for the real thing(s)
241
242 // a series of functions which accept a format string on the form "text %1
243 // more %2 less %3" and a number of templated parameters and spits out the
244 // composited string
245 template <typename T1>
string_compose(const std::string & fmt,const T1 & o1)246 inline std::string string_compose(const std::string &fmt, const T1 &o1)
247 {
248 StringPrivate::Composition c(fmt);
249 c.arg(o1);
250 return c.str();
251 }
252
253 template <typename T1, typename T2>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2)254 inline std::string string_compose(const std::string &fmt,
255 const T1 &o1, const T2 &o2)
256 {
257 StringPrivate::Composition c(fmt);
258 c.arg(o1).arg(o2);
259 return c.str();
260 }
261
262 template <typename T1, typename T2, typename T3>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3)263 inline std::string string_compose(const std::string &fmt,
264 const T1 &o1, const T2 &o2, const T3 &o3)
265 {
266 StringPrivate::Composition c(fmt);
267 c.arg(o1).arg(o2).arg(o3);
268 return c.str();
269 }
270
271 template <typename T1, typename T2, typename T3, typename T4>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4)272 inline std::string string_compose(const std::string &fmt,
273 const T1 &o1, const T2 &o2, const T3 &o3,
274 const T4 &o4)
275 {
276 StringPrivate::Composition c(fmt);
277 c.arg(o1).arg(o2).arg(o3).arg(o4);
278 return c.str();
279 }
280
281 template <typename T1, typename T2, typename T3, typename T4, typename T5>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5)282 inline std::string string_compose(const std::string &fmt,
283 const T1 &o1, const T2 &o2, const T3 &o3,
284 const T4 &o4, const T5 &o5)
285 {
286 StringPrivate::Composition c(fmt);
287 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
288 return c.str();
289 }
290
291 template <typename T1, typename T2, typename T3, typename T4, typename T5,
292 typename T6>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6)293 inline std::string string_compose(const std::string &fmt,
294 const T1 &o1, const T2 &o2, const T3 &o3,
295 const T4 &o4, const T5 &o5, const T6 &o6)
296 {
297 StringPrivate::Composition c(fmt);
298 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
299 return c.str();
300 }
301
302 template <typename T1, typename T2, typename T3, typename T4, typename T5,
303 typename T6, typename T7>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7)304 inline std::string string_compose(const std::string &fmt,
305 const T1 &o1, const T2 &o2, const T3 &o3,
306 const T4 &o4, const T5 &o5, const T6 &o6,
307 const T7 &o7)
308 {
309 StringPrivate::Composition c(fmt);
310 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
311 return c.str();
312 }
313
314 template <typename T1, typename T2, typename T3, typename T4, typename T5,
315 typename T6, typename T7, typename T8>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8)316 inline std::string string_compose(const std::string &fmt,
317 const T1 &o1, const T2 &o2, const T3 &o3,
318 const T4 &o4, const T5 &o5, const T6 &o6,
319 const T7 &o7, const T8 &o8)
320 {
321 StringPrivate::Composition c(fmt);
322 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
323 return c.str();
324 }
325
326 template <typename T1, typename T2, typename T3, typename T4, typename T5,
327 typename T6, typename T7, typename T8, typename T9>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8,const T9 & o9)328 inline std::string string_compose(const std::string &fmt,
329 const T1 &o1, const T2 &o2, const T3 &o3,
330 const T4 &o4, const T5 &o5, const T6 &o6,
331 const T7 &o7, const T8 &o8, const T9 &o9)
332 {
333 StringPrivate::Composition c(fmt);
334 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
335 return c.str();
336 }
337
338 template <typename T1, typename T2, typename T3, typename T4, typename T5,
339 typename T6, typename T7, typename T8, typename T9, typename T10>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8,const T9 & o9,const T10 & o10)340 inline std::string string_compose(const std::string &fmt,
341 const T1 &o1, const T2 &o2, const T3 &o3,
342 const T4 &o4, const T5 &o5, const T6 &o6,
343 const T7 &o7, const T8 &o8, const T9 &o9,
344 const T10 &o10)
345 {
346 StringPrivate::Composition c(fmt);
347 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
348 .arg(o10);
349 return c.str();
350 }
351
352 template <typename T1, typename T2, typename T3, typename T4, typename T5,
353 typename T6, typename T7, typename T8, typename T9, typename T10,
354 typename T11>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8,const T9 & o9,const T10 & o10,const T11 & o11)355 inline std::string string_compose(const std::string &fmt,
356 const T1 &o1, const T2 &o2, const T3 &o3,
357 const T4 &o4, const T5 &o5, const T6 &o6,
358 const T7 &o7, const T8 &o8, const T9 &o9,
359 const T10 &o10, const T11 &o11)
360 {
361 StringPrivate::Composition c(fmt);
362 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
363 .arg(o10).arg(o11);
364 return c.str();
365 }
366
367 template <typename T1, typename T2, typename T3, typename T4, typename T5,
368 typename T6, typename T7, typename T8, typename T9, typename T10,
369 typename T11, typename T12>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8,const T9 & o9,const T10 & o10,const T11 & o11,const T12 & o12)370 inline std::string string_compose(const std::string &fmt,
371 const T1 &o1, const T2 &o2, const T3 &o3,
372 const T4 &o4, const T5 &o5, const T6 &o6,
373 const T7 &o7, const T8 &o8, const T9 &o9,
374 const T10 &o10, const T11 &o11, const T12 &o12)
375 {
376 StringPrivate::Composition c(fmt);
377 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
378 .arg(o10).arg(o11).arg(o12);
379 return c.str();
380 }
381
382 template <typename T1, typename T2, typename T3, typename T4, typename T5,
383 typename T6, typename T7, typename T8, typename T9, typename T10,
384 typename T11, typename T12, typename T13>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8,const T9 & o9,const T10 & o10,const T11 & o11,const T12 & o12,const T13 & o13)385 inline std::string string_compose(const std::string &fmt,
386 const T1 &o1, const T2 &o2, const T3 &o3,
387 const T4 &o4, const T5 &o5, const T6 &o6,
388 const T7 &o7, const T8 &o8, const T9 &o9,
389 const T10 &o10, const T11 &o11, const T12 &o12,
390 const T13 &o13)
391 {
392 StringPrivate::Composition c(fmt);
393 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
394 .arg(o10).arg(o11).arg(o12).arg(o13);
395 return c.str();
396 }
397
398 template <typename T1, typename T2, typename T3, typename T4, typename T5,
399 typename T6, typename T7, typename T8, typename T9, typename T10,
400 typename T11, typename T12, typename T13, typename T14>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8,const T9 & o9,const T10 & o10,const T11 & o11,const T12 & o12,const T13 & o13,const T14 & o14)401 inline std::string string_compose(const std::string &fmt,
402 const T1 &o1, const T2 &o2, const T3 &o3,
403 const T4 &o4, const T5 &o5, const T6 &o6,
404 const T7 &o7, const T8 &o8, const T9 &o9,
405 const T10 &o10, const T11 &o11, const T12 &o12,
406 const T13 &o13, const T14 &o14)
407 {
408 StringPrivate::Composition c(fmt);
409 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
410 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
411 return c.str();
412 }
413
414 template <typename T1, typename T2, typename T3, typename T4, typename T5,
415 typename T6, typename T7, typename T8, typename T9, typename T10,
416 typename T11, typename T12, typename T13, typename T14,
417 typename T15>
string_compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5,const T6 & o6,const T7 & o7,const T8 & o8,const T9 & o9,const T10 & o10,const T11 & o11,const T12 & o12,const T13 & o13,const T14 & o14,const T15 & o15)418 inline std::string string_compose(const std::string &fmt,
419 const T1 &o1, const T2 &o2, const T3 &o3,
420 const T4 &o4, const T5 &o5, const T6 &o6,
421 const T7 &o7, const T8 &o8, const T9 &o9,
422 const T10 &o10, const T11 &o11, const T12 &o12,
423 const T13 &o13, const T14 &o14, const T15 &o15)
424 {
425 StringPrivate::Composition c(fmt);
426 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
427 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
428 return c.str();
429 }
430
431 #endif // STRING_COMPOSE_H
432