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