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