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