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 */
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 //
33 #ifndef STRING_COMPOSE_H
34 #define STRING_COMPOSE_H
36 #include <sstream>
37 #include <string>
38 #include <list>
39 #include <map> // for multimap
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);
51 // supply an replacement argument starting from %1
52 template <typename T>
53 Composition &arg(const T &obj);
55 // compose and return string
56 std::string str() const;
58 private:
59 std::ostringstream os;
60 int arg_no;
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;
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 };
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 }
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;
108 default:
109 return false;
110 }
111 }
114 // implementation of class Composition
115 template <typename T>
116 inline Composition &Composition::arg(const T &obj)
117 {
118 os << obj;
120 std::string rep = os.str();
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;
128 output.insert(pos, rep);
129 }
131 os.str(std::string());
132 //os.clear();
133 ++arg_no;
134 }
136 return *this;
137 }
139 inline Composition::Composition(std::string fmt)
140 : arg_no(1)
141 {
142 std::string::size_type b = 0, i = 0;
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));
156 int n = 1; // number of digits
157 int spec_no = 0;
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]));
165 spec_no /= 10;
166 output_list::iterator pos = output.end();
167 --pos; // safe since we have just inserted a string>
169 specs.insert(specification_map::value_type(spec_no, pos));
171 // jump over spec string
172 i += n;
173 b = i;
174 }
175 else
176 ++i;
177 }
178 else
179 ++i;
180 }
182 if (i - b > 0) // add the rest of the string
183 output.push_back(fmt.substr(b, i - b));
184 }
186 inline std::string Composition::str() const
187 {
188 // assemble string
189 std::string str;
191 for (output_list::const_iterator i = output.begin(), end = output.end();
192 i != end; ++i)
193 str += *i;
195 return str;
196 }
197 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
393 #endif // STRING_COMPOSE_H