b78a8b9ebb172c95aa3f887c9134e9ee2afcf55e
[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 int64_t& obj)
125   {
126     char buffer[64];
127     snprintf(buffer, 64, "%" PRId64, obj);
128     s += buffer;
129   }
130
131   template <>
132   inline void write(std::string& s, const int& obj)
133   {
134     char buffer[64];
135     snprintf(buffer, 64, "%d", obj);
136     s += buffer;
137   }
138
139   template <>
140   inline void write(std::string& s, const unsigned int& obj)
141   {
142     char buffer[64];
143     snprintf(buffer, 64, "%ud", obj);
144     s += buffer;
145   }
146
147   template <>
148   inline void write(std::string& s, const long unsigned int& obj)
149   {
150     char buffer[64];
151     snprintf(buffer, 64, "%lu", obj);
152     s += buffer;
153   }
154
155   template <>
156   inline void write(std::string& s, const float& obj)
157   {
158     char buffer[64];
159     snprintf(buffer, 64, "%f", obj);
160     s += buffer;
161   }
162
163   template <>
164   inline void write(std::string& s, const char& obj)
165   {
166     s += obj;
167   }
168
169   template <>
170   inline void write(std::string& s, const double& obj)
171   {
172     char buffer[64];
173     snprintf(buffer, 64, "%f", obj);
174     s += buffer;
175   }
176
177   template <>
178   inline void write(std::string& s, char const * const & obj)
179   {
180     s += obj;
181   }
182
183   template <>
184   inline void write(std::string& s, char* const & obj)
185   {
186     s += obj;
187   }
188
189   template <>
190   inline void write(std::string& s, const std::string& obj)
191   {
192     s += obj;
193   }
194
195   template <>
196   inline void write(std::string& s, const boost::filesystem::path & obj)
197   {
198     s += obj.string();
199   }
200
201   // implementation of class Composition
202   template <typename T>
203   inline Composition &Composition::arg(const T &obj)
204   {
205     write(os, obj);
206
207     if (!os.empty()) {          // manipulators don't produce output
208       for (specification_map::const_iterator i = specs.lower_bound(arg_no), end = specs.upper_bound(arg_no); i != end; ++i) {
209         output_list::iterator pos = i->second;
210         ++pos;
211
212         output.insert(pos, os);
213       }
214
215       os = "";
216       ++arg_no;
217     }
218
219     return *this;
220   }
221
222   inline Composition::Composition(std::string fmt)
223     : arg_no(1)
224   {
225     std::string::size_type b = 0, i = 0;
226
227     // fill in output with the strings between the %1 %2 %3 etc. and
228     // fill in specs with the positions
229     while (i < fmt.length()) {
230       if (fmt[i] == '%' && i + 1 < fmt.length()) {
231         if (fmt[i + 1] == '%') {        // catch %%
232           fmt.replace(i, 2, "%");
233           ++i;
234         }
235         else if (is_number(fmt[i + 1])) { // aha! a spec!
236           // save string
237           output.push_back(fmt.substr(b, i - b));
238
239           int n = 1;            // number of digits
240           int spec_no = 0;
241
242           do {
243             spec_no += char_to_int(fmt[i + n]);
244             spec_no *= 10;
245             ++n;
246           } while (i + n < fmt.length() && is_number(fmt[i + n]));
247
248           spec_no /= 10;
249           output_list::iterator pos = output.end();
250           --pos;                // safe since we have just inserted a string>
251
252           specs.insert(specification_map::value_type(spec_no, pos));
253
254           // jump over spec string
255           i += n;
256           b = i;
257         }
258         else
259           ++i;
260       }
261       else
262         ++i;
263     }
264
265     if (i - b > 0)              // add the rest of the string
266       output.push_back(fmt.substr(b, i - b));
267   }
268
269   inline std::string Composition::str() const
270   {
271     // assemble string
272     std::string str;
273
274     for (output_list::const_iterator i = output.begin(), end = output.end();
275          i != end; ++i)
276       str += *i;
277
278     return str;
279   }
280 }
281
282 // now for the real thing(s)
283 namespace String
284 {
285   // a series of functions which accept a format string on the form "text %1
286   // more %2 less %3" and a number of templated parameters and spits out the
287   // composited string
288   template <typename T1>
289   inline std::string compose(const std::string &fmt, const T1 &o1)
290   {
291     StringPrivate::Composition c(fmt);
292     c.arg(o1);
293     return c.str();
294   }
295
296   template <typename T1, typename T2>
297   inline std::string compose(const std::string &fmt,
298                              const T1 &o1, const T2 &o2)
299   {
300     StringPrivate::Composition c(fmt);
301     c.arg(o1).arg(o2);
302     return c.str();
303   }
304
305   template <typename T1, typename T2, typename T3>
306   inline std::string compose(const std::string &fmt,
307                              const T1 &o1, const T2 &o2, const T3 &o3)
308   {
309     StringPrivate::Composition c(fmt);
310     c.arg(o1).arg(o2).arg(o3);
311     return c.str();
312   }
313
314   template <typename T1, typename T2, typename T3, typename T4>
315   inline std::string compose(const std::string &fmt,
316                              const T1 &o1, const T2 &o2, const T3 &o3,
317                              const T4 &o4)
318   {
319     StringPrivate::Composition c(fmt);
320     c.arg(o1).arg(o2).arg(o3).arg(o4);
321     return c.str();
322   }
323
324   template <typename T1, typename T2, typename T3, typename T4, typename T5>
325   inline std::string compose(const std::string &fmt,
326                              const T1 &o1, const T2 &o2, const T3 &o3,
327                              const T4 &o4, const T5 &o5)
328   {
329     StringPrivate::Composition c(fmt);
330     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
331     return c.str();
332   }
333
334   template <typename T1, typename T2, typename T3, typename T4, typename T5,
335             typename T6>
336   inline std::string compose(const std::string &fmt,
337                              const T1 &o1, const T2 &o2, const T3 &o3,
338                              const T4 &o4, const T5 &o5, const T6 &o6)
339   {
340     StringPrivate::Composition c(fmt);
341     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
342     return c.str();
343   }
344
345   template <typename T1, typename T2, typename T3, typename T4, typename T5,
346             typename T6, typename T7>
347   inline std::string compose(const std::string &fmt,
348                              const T1 &o1, const T2 &o2, const T3 &o3,
349                              const T4 &o4, const T5 &o5, const T6 &o6,
350                              const T7 &o7)
351   {
352     StringPrivate::Composition c(fmt);
353     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
354     return c.str();
355   }
356
357   template <typename T1, typename T2, typename T3, typename T4, typename T5,
358             typename T6, typename T7, typename T8>
359   inline std::string compose(const std::string &fmt,
360                              const T1 &o1, const T2 &o2, const T3 &o3,
361                              const T4 &o4, const T5 &o5, const T6 &o6,
362                              const T7 &o7, const T8 &o8)
363   {
364     StringPrivate::Composition c(fmt);
365     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
366     return c.str();
367   }
368
369   template <typename T1, typename T2, typename T3, typename T4, typename T5,
370             typename T6, typename T7, typename T8, typename T9>
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, const T8 &o8, const T9 &o9)
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     return c.str();
379   }
380
381   template <typename T1, typename T2, typename T3, typename T4, typename T5,
382             typename T6, typename T7, typename T8, typename T9, typename T10>
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, const T9 &o9,
387                              const T10 &o10)
388   {
389     StringPrivate::Composition c(fmt);
390     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
391       .arg(o10);
392     return c.str();
393   }
394
395   template <typename T1, typename T2, typename T3, typename T4, typename T5,
396             typename T6, typename T7, typename T8, typename T9, typename T10,
397             typename T11>
398   inline std::string compose(const std::string &fmt,
399                              const T1 &o1, const T2 &o2, const T3 &o3,
400                              const T4 &o4, const T5 &o5, const T6 &o6,
401                              const T7 &o7, const T8 &o8, const T9 &o9,
402                              const T10 &o10, const T11 &o11)
403   {
404     StringPrivate::Composition c(fmt);
405     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
406       .arg(o10).arg(o11);
407     return c.str();
408   }
409
410   template <typename T1, typename T2, typename T3, typename T4, typename T5,
411             typename T6, typename T7, typename T8, typename T9, typename T10,
412             typename T11, typename T12>
413   inline std::string compose(const std::string &fmt,
414                              const T1 &o1, const T2 &o2, const T3 &o3,
415                              const T4 &o4, const T5 &o5, const T6 &o6,
416                              const T7 &o7, const T8 &o8, const T9 &o9,
417                              const T10 &o10, const T11 &o11, const T12 &o12)
418   {
419     StringPrivate::Composition c(fmt);
420     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
421       .arg(o10).arg(o11).arg(o12);
422     return c.str();
423   }
424
425   template <typename T1, typename T2, typename T3, typename T4, typename T5,
426             typename T6, typename T7, typename T8, typename T9, typename T10,
427             typename T11, typename T12, typename T13>
428   inline std::string compose(const std::string &fmt,
429                              const T1 &o1, const T2 &o2, const T3 &o3,
430                              const T4 &o4, const T5 &o5, const T6 &o6,
431                              const T7 &o7, const T8 &o8, const T9 &o9,
432                              const T10 &o10, const T11 &o11, const T12 &o12,
433                              const T13 &o13)
434   {
435     StringPrivate::Composition c(fmt);
436     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
437       .arg(o10).arg(o11).arg(o12).arg(o13);
438     return c.str();
439   }
440
441   template <typename T1, typename T2, typename T3, typename T4, typename T5,
442             typename T6, typename T7, typename T8, typename T9, typename T10,
443             typename T11, typename T12, typename T13, typename T14>
444   inline std::string compose(const std::string &fmt,
445                              const T1 &o1, const T2 &o2, const T3 &o3,
446                              const T4 &o4, const T5 &o5, const T6 &o6,
447                              const T7 &o7, const T8 &o8, const T9 &o9,
448                              const T10 &o10, const T11 &o11, const T12 &o12,
449                              const T13 &o13, const T14 &o14)
450   {
451     StringPrivate::Composition c(fmt);
452     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
453       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
454     return c.str();
455   }
456
457   template <typename T1, typename T2, typename T3, typename T4, typename T5,
458             typename T6, typename T7, typename T8, typename T9, typename T10,
459             typename T11, typename T12, typename T13, typename T14,
460             typename T15>
461   inline std::string compose(const std::string &fmt,
462                              const T1 &o1, const T2 &o2, const T3 &o3,
463                              const T4 &o4, const T5 &o5, const T6 &o6,
464                              const T7 &o7, const T8 &o8, const T9 &o9,
465                              const T10 &o10, const T11 &o11, const T12 &o12,
466                              const T13 &o13, const T14 &o14, const T15 &o15)
467   {
468     StringPrivate::Composition c(fmt);
469     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
470       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
471     return c.str();
472   }
473 }
474
475
476 #endif // STRING_COMPOSE_H