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