More Windows hacks.
[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 #include <cstdio>
43
44 namespace StringPrivate
45 {
46   // the actual composition class - using string::compose is cleaner, so we
47   // hide it here
48   class Composition
49   {
50   public:
51     // initialize and prepare format string on the form "text %1 text %2 etc."
52     explicit Composition(std::string fmt);
53
54     // supply an replacement argument starting from %1
55     template <typename T>
56     Composition &arg(const T &obj);
57
58     // compose and return string
59     std::string str() const;
60
61   private:
62     std::string os;
63     int arg_no;
64
65     // we store the output as a list - when the output string is requested, the
66     // list is concatenated to a string; this way we can keep iterators into
67     // the list instead of into a string where they're possibly invalidated on
68     // inserting a specification string
69     typedef std::list<std::string> output_list;
70     output_list output;
71
72     // the initial parse of the format string fills in the specification map
73     // with positions for each of the various %?s
74     typedef std::multimap<int, output_list::iterator> specification_map;
75     specification_map specs;
76   };
77
78   // helper for converting spec string numbers
79   inline int char_to_int(char c)
80   {
81     switch (c) {
82     case '0': return 0;
83     case '1': return 1;
84     case '2': return 2;
85     case '3': return 3;
86     case '4': return 4;
87     case '5': return 5;
88     case '6': return 6;
89     case '7': return 7;
90     case '8': return 8;
91     case '9': return 9;
92     default: return -1000;
93     }
94   }
95
96   inline bool is_number(int n)
97   {
98     switch (n) {
99     case '0':
100     case '1':
101     case '2':
102     case '3':
103     case '4':
104     case '5':
105     case '6':
106     case '7':
107     case '8':
108     case '9':
109       return true;
110
111     default:
112       return false;
113     }
114   }
115
116   template <typename T>
117   inline void write(std::string& s, const T& obj)
118   {
119     /* Assume anything not specialized has a to_string() method */
120     s += to_string (obj);
121   }
122
123   template <>
124   inline void write(std::string& s, const int32_t& obj)
125   {
126     char buffer[64];
127 #ifdef DCPOMATIC_WINDOWS
128     __mingw_snprintf(buffer, 64, "%" PRId32, obj);
129 #else
130     snprintf(buffer, 64, "%" PRId32, obj);
131 #endif
132     s += buffer;
133   }
134
135   template <>
136   inline void write(std::string& s, const uint32_t& obj)
137   {
138     char buffer[64];
139 #ifdef DCPOMATIC_WINDOWS
140     __mingw_snprintf(buffer, 64, "%" PRIu32, obj);
141 #else
142     snprintf(buffer, 64, "%" PRIu32, obj);
143 #endif
144     s += buffer;
145   }
146
147   template <>
148   inline void write(std::string& s, const int64_t& obj)
149   {
150     char buffer[64];
151 #ifdef DCPOMATIC_WINDOWS
152     __mingw_snprintf(buffer, 64, "%" PRId64, obj);
153 #else
154     snprintf(buffer, 64, "%" PRId64, obj);
155 #endif
156     s += buffer;
157   }
158
159   template <>
160   inline void write(std::string& s, const uint64_t& obj)
161   {
162     char buffer[64];
163 #ifdef DCPOMATIC_WINDOWS
164     __mingw_snprintf(buffer, 64, "%" PRIu64, obj);
165 #else
166     snprintf(buffer, 64, "%" PRIu64, obj);
167 #endif
168     s += buffer;
169   }
170
171   template <>
172   inline void write(std::string& s, const float& obj)
173   {
174     char buffer[64];
175     snprintf(buffer, 64, "%f", obj);
176     s += buffer;
177   }
178
179   template <>
180   inline void write(std::string& s, const char& obj)
181   {
182     s += obj;
183   }
184
185   template <>
186   inline void write(std::string& s, const double& obj)
187   {
188     char buffer[64];
189     snprintf(buffer, 64, "%f", obj);
190     s += buffer;
191   }
192
193   template <>
194   inline void write(std::string& s, char const * const & obj)
195   {
196     s += obj;
197   }
198
199   template <>
200   inline void write(std::string& s, char* const & obj)
201   {
202     s += obj;
203   }
204
205   template <>
206   inline void write(std::string& s, wchar_t[] const & obj)
207   {
208     std::wstring ws (&obj);
209     std::string w (ws.begin(), ws.end());
210     s += w;
211   }
212
213   template <>
214   inline void write(std::string& s, const std::string& obj)
215   {
216     s += obj;
217   }
218
219   template <>
220   inline void write(std::string& s, const boost::filesystem::path & obj)
221   {
222     s += obj.string();
223   }
224
225   // implementation of class Composition
226   template <typename T>
227   inline Composition &Composition::arg(const T &obj)
228   {
229     write(os, obj);
230
231     if (!os.empty()) {          // manipulators don't produce output
232       for (specification_map::const_iterator i = specs.lower_bound(arg_no), end = specs.upper_bound(arg_no); i != end; ++i) {
233         output_list::iterator pos = i->second;
234         ++pos;
235
236         output.insert(pos, os);
237       }
238
239       os = "";
240       ++arg_no;
241     }
242
243     return *this;
244   }
245
246   inline Composition::Composition(std::string fmt)
247     : arg_no(1)
248   {
249     std::string::size_type b = 0, i = 0;
250
251     // fill in output with the strings between the %1 %2 %3 etc. and
252     // fill in specs with the positions
253     while (i < fmt.length()) {
254       if (fmt[i] == '%' && i + 1 < fmt.length()) {
255         if (fmt[i + 1] == '%') {        // catch %%
256           fmt.replace(i, 2, "%");
257           ++i;
258         }
259         else if (is_number(fmt[i + 1])) { // aha! a spec!
260           // save string
261           output.push_back(fmt.substr(b, i - b));
262
263           int n = 1;            // number of digits
264           int spec_no = 0;
265
266           do {
267             spec_no += char_to_int(fmt[i + n]);
268             spec_no *= 10;
269             ++n;
270           } while (i + n < fmt.length() && is_number(fmt[i + n]));
271
272           spec_no /= 10;
273           output_list::iterator pos = output.end();
274           --pos;                // safe since we have just inserted a string>
275
276           specs.insert(specification_map::value_type(spec_no, pos));
277
278           // jump over spec string
279           i += n;
280           b = i;
281         }
282         else
283           ++i;
284       }
285       else
286         ++i;
287     }
288
289     if (i - b > 0)              // add the rest of the string
290       output.push_back(fmt.substr(b, i - b));
291   }
292
293   inline std::string Composition::str() const
294   {
295     // assemble string
296     std::string str;
297
298     for (output_list::const_iterator i = output.begin(), end = output.end();
299          i != end; ++i)
300       str += *i;
301
302     return str;
303   }
304 }
305
306 // now for the real thing(s)
307 namespace String
308 {
309   // a series of functions which accept a format string on the form "text %1
310   // more %2 less %3" and a number of templated parameters and spits out the
311   // composited string
312   template <typename T1>
313   inline std::string compose(const std::string &fmt, const T1 &o1)
314   {
315     StringPrivate::Composition c(fmt);
316     c.arg(o1);
317     return c.str();
318   }
319
320   template <typename T1, typename T2>
321   inline std::string compose(const std::string &fmt,
322                              const T1 &o1, const T2 &o2)
323   {
324     StringPrivate::Composition c(fmt);
325     c.arg(o1).arg(o2);
326     return c.str();
327   }
328
329   template <typename T1, typename T2, typename T3>
330   inline std::string compose(const std::string &fmt,
331                              const T1 &o1, const T2 &o2, const T3 &o3)
332   {
333     StringPrivate::Composition c(fmt);
334     c.arg(o1).arg(o2).arg(o3);
335     return c.str();
336   }
337
338   template <typename T1, typename T2, typename T3, typename T4>
339   inline std::string compose(const std::string &fmt,
340                              const T1 &o1, const T2 &o2, const T3 &o3,
341                              const T4 &o4)
342   {
343     StringPrivate::Composition c(fmt);
344     c.arg(o1).arg(o2).arg(o3).arg(o4);
345     return c.str();
346   }
347
348   template <typename T1, typename T2, typename T3, typename T4, typename T5>
349   inline std::string compose(const std::string &fmt,
350                              const T1 &o1, const T2 &o2, const T3 &o3,
351                              const T4 &o4, const T5 &o5)
352   {
353     StringPrivate::Composition c(fmt);
354     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
355     return c.str();
356   }
357
358   template <typename T1, typename T2, typename T3, typename T4, typename T5,
359             typename T6>
360   inline std::string compose(const std::string &fmt,
361                              const T1 &o1, const T2 &o2, const T3 &o3,
362                              const T4 &o4, const T5 &o5, const T6 &o6)
363   {
364     StringPrivate::Composition c(fmt);
365     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
366     return c.str();
367   }
368
369   template <typename T1, typename T2, typename T3, typename T4, typename T5,
370             typename T6, typename T7>
371   inline std::string compose(const std::string &fmt,
372                              const T1 &o1, const T2 &o2, const T3 &o3,
373                              const T4 &o4, const T5 &o5, const T6 &o6,
374                              const T7 &o7)
375   {
376     StringPrivate::Composition c(fmt);
377     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
378     return c.str();
379   }
380
381   template <typename T1, typename T2, typename T3, typename T4, typename T5,
382             typename T6, typename T7, typename T8>
383   inline std::string compose(const std::string &fmt,
384                              const T1 &o1, const T2 &o2, const T3 &o3,
385                              const T4 &o4, const T5 &o5, const T6 &o6,
386                              const T7 &o7, const T8 &o8)
387   {
388     StringPrivate::Composition c(fmt);
389     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
390     return c.str();
391   }
392
393   template <typename T1, typename T2, typename T3, typename T4, typename T5,
394             typename T6, typename T7, typename T8, typename T9>
395   inline std::string compose(const std::string &fmt,
396                              const T1 &o1, const T2 &o2, const T3 &o3,
397                              const T4 &o4, const T5 &o5, const T6 &o6,
398                              const T7 &o7, const T8 &o8, const T9 &o9)
399   {
400     StringPrivate::Composition c(fmt);
401     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
402     return c.str();
403   }
404
405   template <typename T1, typename T2, typename T3, typename T4, typename T5,
406             typename T6, typename T7, typename T8, typename T9, typename T10>
407   inline std::string compose(const std::string &fmt,
408                              const T1 &o1, const T2 &o2, const T3 &o3,
409                              const T4 &o4, const T5 &o5, const T6 &o6,
410                              const T7 &o7, const T8 &o8, const T9 &o9,
411                              const T10 &o10)
412   {
413     StringPrivate::Composition c(fmt);
414     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
415       .arg(o10);
416     return c.str();
417   }
418
419   template <typename T1, typename T2, typename T3, typename T4, typename T5,
420             typename T6, typename T7, typename T8, typename T9, typename T10,
421             typename T11>
422   inline std::string compose(const std::string &fmt,
423                              const T1 &o1, const T2 &o2, const T3 &o3,
424                              const T4 &o4, const T5 &o5, const T6 &o6,
425                              const T7 &o7, const T8 &o8, const T9 &o9,
426                              const T10 &o10, const T11 &o11)
427   {
428     StringPrivate::Composition c(fmt);
429     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
430       .arg(o10).arg(o11);
431     return c.str();
432   }
433
434   template <typename T1, typename T2, typename T3, typename T4, typename T5,
435             typename T6, typename T7, typename T8, typename T9, typename T10,
436             typename T11, typename T12>
437   inline std::string compose(const std::string &fmt,
438                              const T1 &o1, const T2 &o2, const T3 &o3,
439                              const T4 &o4, const T5 &o5, const T6 &o6,
440                              const T7 &o7, const T8 &o8, const T9 &o9,
441                              const T10 &o10, const T11 &o11, const T12 &o12)
442   {
443     StringPrivate::Composition c(fmt);
444     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
445       .arg(o10).arg(o11).arg(o12);
446     return c.str();
447   }
448
449   template <typename T1, typename T2, typename T3, typename T4, typename T5,
450             typename T6, typename T7, typename T8, typename T9, typename T10,
451             typename T11, typename T12, typename T13>
452   inline std::string compose(const std::string &fmt,
453                              const T1 &o1, const T2 &o2, const T3 &o3,
454                              const T4 &o4, const T5 &o5, const T6 &o6,
455                              const T7 &o7, const T8 &o8, const T9 &o9,
456                              const T10 &o10, const T11 &o11, const T12 &o12,
457                              const T13 &o13)
458   {
459     StringPrivate::Composition c(fmt);
460     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
461       .arg(o10).arg(o11).arg(o12).arg(o13);
462     return c.str();
463   }
464
465   template <typename T1, typename T2, typename T3, typename T4, typename T5,
466             typename T6, typename T7, typename T8, typename T9, typename T10,
467             typename T11, typename T12, typename T13, typename T14>
468   inline std::string compose(const std::string &fmt,
469                              const T1 &o1, const T2 &o2, const T3 &o3,
470                              const T4 &o4, const T5 &o5, const T6 &o6,
471                              const T7 &o7, const T8 &o8, const T9 &o9,
472                              const T10 &o10, const T11 &o11, const T12 &o12,
473                              const T13 &o13, const T14 &o14)
474   {
475     StringPrivate::Composition c(fmt);
476     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
477       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
478     return c.str();
479   }
480
481   template <typename T1, typename T2, typename T3, typename T4, typename T5,
482             typename T6, typename T7, typename T8, typename T9, typename T10,
483             typename T11, typename T12, typename T13, typename T14,
484             typename T15>
485   inline std::string compose(const std::string &fmt,
486                              const T1 &o1, const T2 &o2, const T3 &o3,
487                              const T4 &o4, const T5 &o5, const T6 &o6,
488                              const T7 &o7, const T8 &o8, const T9 &o9,
489                              const T10 &o10, const T11 &o11, const T12 &o12,
490                              const T13 &o13, const T14 &o14, const T15 &o15)
491   {
492     StringPrivate::Composition c(fmt);
493     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
494       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
495     return c.str();
496   }
497 }
498
499
500 #endif // STRING_COMPOSE_H