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 St, Fifth Floor, Boston, MA 02110-1301
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 char_to_int( char c )
78 {
79   switch ( c )
80   {
81   case '0':
82     return 0;
83   case '1':
84     return 1;
85   case '2':
86     return 2;
87   case '3':
88     return 3;
89   case '4':
90     return 4;
91   case '5':
92     return 5;
93   case '6':
94     return 6;
95   case '7':
96     return 7;
97   case '8':
98     return 8;
99   case '9':
100     return 9;
101   default:
102     return -1000;
103   }
104 }
105 
106 inline bool
is_number(int n)107 is_number( int n )
108 {
109   switch ( n )
110   {
111   case '0':
112   case '1':
113   case '2':
114   case '3':
115   case '4':
116   case '5':
117   case '6':
118   case '7':
119   case '8':
120   case '9':
121     return true;
122 
123   default:
124     return false;
125   }
126 }
127 
128 
129 // implementation of class Composition
130 template < typename T >
131 inline Composition&
arg(const T & obj)132 Composition::arg( const T& obj )
133 {
134   os << obj;
135 
136   std::string rep = os.str();
137 
138   if ( !rep.empty() )
139   { // manipulators don't produce output
140     for ( specification_map::const_iterator i = specs.lower_bound( arg_no ),
141                                             end = specs.upper_bound( arg_no );
142           i != end;
143           ++i )
144     {
145       output_list::iterator pos = i->second;
146       ++pos;
147 
148       output.insert( pos, rep );
149     }
150 
151     os.str( std::string() );
152     // os.clear();
153     ++arg_no;
154   }
155 
156   return *this;
157 }
158 
Composition(std::string fmt)159 inline Composition::Composition( std::string fmt )
160   : arg_no( 1 )
161 {
162   std::string::size_type b = 0, i = 0;
163 
164   // fill in output with the strings between the %1 %2 %3 etc. and
165   // fill in specs with the positions
166   while ( i < fmt.length() )
167   {
168     if ( fmt[ i ] == '%' && i + 1 < fmt.length() )
169     {
170       if ( fmt[ i + 1 ] == '%' )
171       { // catch %%
172         fmt.replace( i, 2, "%" );
173         ++i;
174       }
175       else if ( is_number( fmt[ i + 1 ] ) )
176       { // aha! a spec!
177         // save string
178         output.push_back( fmt.substr( b, i - b ) );
179 
180         int n = 1; // number of digits
181         int spec_no = 0;
182 
183         do
184         {
185           spec_no += char_to_int( fmt[ i + n ] );
186           spec_no *= 10;
187           ++n;
188         } while ( i + n < fmt.length() && is_number( fmt[ i + n ] ) );
189 
190         spec_no /= 10;
191         output_list::iterator pos = output.end();
192         --pos; // safe since we have just inserted a string>
193 
194         specs.insert( specification_map::value_type( spec_no, pos ) );
195 
196         // jump over spec string
197         i += n;
198         b = i;
199       }
200       else
201         ++i;
202     }
203     else
204       ++i;
205   }
206 
207   if ( i - b > 0 ) // add the rest of the string
208     output.push_back( fmt.substr( b, i - b ) );
209 }
210 
211 inline std::string
str() const212 Composition::str() const
213 {
214   // assemble string
215   std::string str;
216 
217   for ( output_list::const_iterator i = output.begin(), end = output.end();
218         i != end;
219         ++i )
220     str += *i;
221 
222   return str;
223 }
224 }
225 
226 // now for the real thing(s)
227 namespace String
228 {
229 // a series of functions which accept a format string on the form "text %1
230 // more %2 less %3" and a number of templated parameters and spits out the
231 // composited string
232 template < typename T1 >
233 inline std::string
compose(const std::string & fmt,const T1 & o1)234 compose( const std::string& fmt, const T1& o1 )
235 {
236   StringPrivate::Composition c( fmt );
237   c.arg( o1 );
238   return c.str();
239 }
240 
241 template < typename T1, typename T2 >
242 inline std::string
compose(const std::string & fmt,const T1 & o1,const T2 & o2)243 compose( const std::string& fmt, const T1& o1, const T2& o2 )
244 {
245   StringPrivate::Composition c( fmt );
246   c.arg( o1 ).arg( o2 );
247   return c.str();
248 }
249 
250 template < typename T1, typename T2, typename T3 >
251 inline std::string
compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3)252 compose( const std::string& fmt, const T1& o1, const T2& o2, const T3& o3 )
253 {
254   StringPrivate::Composition c( fmt );
255   c.arg( o1 ).arg( o2 ).arg( o3 );
256   return c.str();
257 }
258 
259 template < typename T1, typename T2, typename T3, typename T4 >
260 inline std::string
compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4)261 compose( const std::string& fmt,
262   const T1& o1,
263   const T2& o2,
264   const T3& o3,
265   const T4& o4 )
266 {
267   StringPrivate::Composition c( fmt );
268   c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 );
269   return c.str();
270 }
271 
272 template < typename T1, typename T2, typename T3, typename T4, typename T5 >
273 inline std::string
compose(const std::string & fmt,const T1 & o1,const T2 & o2,const T3 & o3,const T4 & o4,const T5 & o5)274 compose( const std::string& fmt,
275   const T1& o1,
276   const T2& o2,
277   const T3& o3,
278   const T4& o4,
279   const T5& o5 )
280 {
281   StringPrivate::Composition c( fmt );
282   c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 );
283   return c.str();
284 }
285 
286 template < typename T1,
287   typename T2,
288   typename T3,
289   typename T4,
290   typename T5,
291   typename T6 >
292 inline std::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 compose( const std::string& fmt,
294   const T1& o1,
295   const T2& o2,
296   const T3& o3,
297   const T4& o4,
298   const T5& o5,
299   const T6& o6 )
300 {
301   StringPrivate::Composition c( fmt );
302   c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 ).arg( o6 );
303   return c.str();
304 }
305 
306 template < typename T1,
307   typename T2,
308   typename T3,
309   typename T4,
310   typename T5,
311   typename T6,
312   typename T7 >
313 inline std::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)314 compose( const std::string& fmt,
315   const T1& o1,
316   const T2& o2,
317   const T3& o3,
318   const T4& o4,
319   const T5& o5,
320   const T6& o6,
321   const T7& o7 )
322 {
323   StringPrivate::Composition c( fmt );
324   c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 ).arg( o6 ).arg( o7 );
325   return c.str();
326 }
327 
328 template < typename T1,
329   typename T2,
330   typename T3,
331   typename T4,
332   typename T5,
333   typename T6,
334   typename T7,
335   typename T8 >
336 inline std::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)337 compose( const std::string& fmt,
338   const T1& o1,
339   const T2& o2,
340   const T3& o3,
341   const T4& o4,
342   const T5& o5,
343   const T6& o6,
344   const T7& o7,
345   const T8& o8 )
346 {
347   StringPrivate::Composition c( fmt );
348   c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 ).arg( o6 ).arg( o7 ).arg(
349     o8 );
350   return c.str();
351 }
352 
353 template < typename T1,
354   typename T2,
355   typename T3,
356   typename T4,
357   typename T5,
358   typename T6,
359   typename T7,
360   typename T8,
361   typename T9 >
362 inline std::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)363 compose( const std::string& fmt,
364   const T1& o1,
365   const T2& o2,
366   const T3& o3,
367   const T4& o4,
368   const T5& o5,
369   const T6& o6,
370   const T7& o7,
371   const T8& o8,
372   const T9& o9 )
373 {
374   StringPrivate::Composition c( fmt );
375   c.arg( o1 )
376     .arg( o2 )
377     .arg( o3 )
378     .arg( o4 )
379     .arg( o5 )
380     .arg( o6 )
381     .arg( o7 )
382     .arg( o8 )
383     .arg( o9 );
384   return c.str();
385 }
386 
387 template < typename T1,
388   typename T2,
389   typename T3,
390   typename T4,
391   typename T5,
392   typename T6,
393   typename T7,
394   typename T8,
395   typename T9,
396   typename T10 >
397 inline std::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)398 compose( const std::string& fmt,
399   const T1& o1,
400   const T2& o2,
401   const T3& o3,
402   const T4& o4,
403   const T5& o5,
404   const T6& o6,
405   const T7& o7,
406   const T8& o8,
407   const T9& o9,
408   const T10& o10 )
409 {
410   StringPrivate::Composition c( fmt );
411   c.arg( o1 )
412     .arg( o2 )
413     .arg( o3 )
414     .arg( o4 )
415     .arg( o5 )
416     .arg( o6 )
417     .arg( o7 )
418     .arg( o8 )
419     .arg( o9 )
420     .arg( o10 );
421   return c.str();
422 }
423 
424 template < typename T1,
425   typename T2,
426   typename T3,
427   typename T4,
428   typename T5,
429   typename T6,
430   typename T7,
431   typename T8,
432   typename T9,
433   typename T10,
434   typename T11 >
435 inline std::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)436 compose( const std::string& fmt,
437   const T1& o1,
438   const T2& o2,
439   const T3& o3,
440   const T4& o4,
441   const T5& o5,
442   const T6& o6,
443   const T7& o7,
444   const T8& o8,
445   const T9& o9,
446   const T10& o10,
447   const T11& o11 )
448 {
449   StringPrivate::Composition c( fmt );
450   c.arg( o1 )
451     .arg( o2 )
452     .arg( o3 )
453     .arg( o4 )
454     .arg( o5 )
455     .arg( o6 )
456     .arg( o7 )
457     .arg( o8 )
458     .arg( o9 )
459     .arg( o10 )
460     .arg( o11 );
461   return c.str();
462 }
463 
464 template < typename T1,
465   typename T2,
466   typename T3,
467   typename T4,
468   typename T5,
469   typename T6,
470   typename T7,
471   typename T8,
472   typename T9,
473   typename T10,
474   typename T11,
475   typename T12 >
476 inline std::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)477 compose( const std::string& fmt,
478   const T1& o1,
479   const T2& o2,
480   const T3& o3,
481   const T4& o4,
482   const T5& o5,
483   const T6& o6,
484   const T7& o7,
485   const T8& o8,
486   const T9& o9,
487   const T10& o10,
488   const T11& o11,
489   const T12& o12 )
490 {
491   StringPrivate::Composition c( fmt );
492   c.arg( o1 )
493     .arg( o2 )
494     .arg( o3 )
495     .arg( o4 )
496     .arg( o5 )
497     .arg( o6 )
498     .arg( o7 )
499     .arg( o8 )
500     .arg( o9 )
501     .arg( o10 )
502     .arg( o11 )
503     .arg( o12 );
504   return c.str();
505 }
506 
507 template < typename T1,
508   typename T2,
509   typename T3,
510   typename T4,
511   typename T5,
512   typename T6,
513   typename T7,
514   typename T8,
515   typename T9,
516   typename T10,
517   typename T11,
518   typename T12,
519   typename T13 >
520 inline std::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)521 compose( const std::string& fmt,
522   const T1& o1,
523   const T2& o2,
524   const T3& o3,
525   const T4& o4,
526   const T5& o5,
527   const T6& o6,
528   const T7& o7,
529   const T8& o8,
530   const T9& o9,
531   const T10& o10,
532   const T11& o11,
533   const T12& o12,
534   const T13& o13 )
535 {
536   StringPrivate::Composition c( fmt );
537   c.arg( o1 )
538     .arg( o2 )
539     .arg( o3 )
540     .arg( o4 )
541     .arg( o5 )
542     .arg( o6 )
543     .arg( o7 )
544     .arg( o8 )
545     .arg( o9 )
546     .arg( o10 )
547     .arg( o11 )
548     .arg( o12 )
549     .arg( o13 );
550   return c.str();
551 }
552 
553 template < typename T1,
554   typename T2,
555   typename T3,
556   typename T4,
557   typename T5,
558   typename T6,
559   typename T7,
560   typename T8,
561   typename T9,
562   typename T10,
563   typename T11,
564   typename T12,
565   typename T13,
566   typename T14 >
567 inline std::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)568 compose( const std::string& fmt,
569   const T1& o1,
570   const T2& o2,
571   const T3& o3,
572   const T4& o4,
573   const T5& o5,
574   const T6& o6,
575   const T7& o7,
576   const T8& o8,
577   const T9& o9,
578   const T10& o10,
579   const T11& o11,
580   const T12& o12,
581   const T13& o13,
582   const T14& o14 )
583 {
584   StringPrivate::Composition c( fmt );
585   c.arg( o1 )
586     .arg( o2 )
587     .arg( o3 )
588     .arg( o4 )
589     .arg( o5 )
590     .arg( o6 )
591     .arg( o7 )
592     .arg( o8 )
593     .arg( o9 )
594     .arg( o10 )
595     .arg( o11 )
596     .arg( o12 )
597     .arg( o13 )
598     .arg( o14 );
599   return c.str();
600 }
601 
602 template < typename T1,
603   typename T2,
604   typename T3,
605   typename T4,
606   typename T5,
607   typename T6,
608   typename T7,
609   typename T8,
610   typename T9,
611   typename T10,
612   typename T11,
613   typename T12,
614   typename T13,
615   typename T14,
616   typename T15 >
617 inline std::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)618 compose( const std::string& fmt,
619   const T1& o1,
620   const T2& o2,
621   const T3& o3,
622   const T4& o4,
623   const T5& o5,
624   const T6& o6,
625   const T7& o7,
626   const T8& o8,
627   const T9& o9,
628   const T10& o10,
629   const T11& o11,
630   const T12& o12,
631   const T13& o13,
632   const T14& o14,
633   const T15& o15 )
634 {
635   StringPrivate::Composition c( fmt );
636   c.arg( o1 )
637     .arg( o2 )
638     .arg( o3 )
639     .arg( o4 )
640     .arg( o5 )
641     .arg( o6 )
642     .arg( o7 )
643     .arg( o8 )
644     .arg( o9 )
645     .arg( o10 )
646     .arg( o11 )
647     .arg( o12 )
648     .arg( o13 )
649     .arg( o14 )
650     .arg( o15 );
651   return c.str();
652 }
653 }
654 
655 
656 #endif // STRING_COMPOSE_H
657