432 lines
12 KiB
C++
432 lines
12 KiB
C++
/* Defines String::compose(fmt, arg...) for easy, i18n-friendly
|
|
* composition of strings.
|
|
*
|
|
* Version 1.0.
|
|
*
|
|
* Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
//
|
|
// Basic usage is like
|
|
//
|
|
// std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
|
|
//
|
|
// See http://www.cs.auc.dk/~olau/compose/ or the included README.compose for
|
|
// more details.
|
|
//
|
|
|
|
#ifndef STRING_COMPOSE_H
|
|
#define STRING_COMPOSE_H
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <list>
|
|
#include <map> // for multimap
|
|
|
|
#include "pbd/libpbd_visibility.h"
|
|
|
|
namespace StringPrivate
|
|
{
|
|
// the actual composition class - using string::compose is cleaner, so we
|
|
// hide it here
|
|
class LIBPBD_API Composition
|
|
{
|
|
public:
|
|
// initialize and prepare format string on the form "text %1 text %2 etc."
|
|
explicit Composition(std::string fmt);
|
|
|
|
// supply an replacement argument starting from %1
|
|
template <typename T>
|
|
Composition &arg(const T &obj);
|
|
|
|
// specialization to catch strings (C++ and C)
|
|
Composition &arg(const std::string &str);
|
|
Composition &arg(char const * const cstr);
|
|
|
|
// compose and return string
|
|
std::string str() const;
|
|
|
|
private:
|
|
std::ostringstream os;
|
|
int arg_no;
|
|
|
|
// we store the output as a list - when the output string is requested, the
|
|
// list is concatenated to a string; this way we can keep iterators into
|
|
// the list instead of into a string where they're possibly invalidated on
|
|
// inserting a specification string
|
|
typedef std::list<std::string> output_list;
|
|
output_list output;
|
|
|
|
// the initial parse of the format string fills in the specification map
|
|
// with positions for each of the various %?s
|
|
typedef std::multimap<int, output_list::iterator> specification_map;
|
|
specification_map specs;
|
|
};
|
|
|
|
// helper for converting spec string numbers
|
|
inline int char_to_int(char c)
|
|
{
|
|
switch (c) {
|
|
case '0': return 0;
|
|
case '1': return 1;
|
|
case '2': return 2;
|
|
case '3': return 3;
|
|
case '4': return 4;
|
|
case '5': return 5;
|
|
case '6': return 6;
|
|
case '7': return 7;
|
|
case '8': return 8;
|
|
case '9': return 9;
|
|
default: return -1000;
|
|
}
|
|
}
|
|
|
|
inline bool is_number(int n)
|
|
{
|
|
switch (n) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// implementation of class Composition
|
|
template <typename T>
|
|
inline Composition &Composition::arg(const T &obj)
|
|
{
|
|
os << obj;
|
|
|
|
std::string rep = os.str();
|
|
|
|
if (!rep.empty()) { // manipulators don't produce output
|
|
for (specification_map::const_iterator i = specs.lower_bound(arg_no),
|
|
end = specs.upper_bound(arg_no); i != end; ++i) {
|
|
output_list::iterator pos = i->second;
|
|
++pos;
|
|
|
|
output.insert(pos, rep);
|
|
}
|
|
|
|
os.str(std::string());
|
|
//os.clear();
|
|
++arg_no;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline Composition &Composition::arg(const std::string &str)
|
|
{
|
|
/* specialization to ensure that empty strings show up
|
|
* in the output
|
|
*/
|
|
for (specification_map::const_iterator i = specs.lower_bound(arg_no),
|
|
end = specs.upper_bound(arg_no); i != end; ++i) {
|
|
output_list::iterator pos = i->second;
|
|
++pos;
|
|
|
|
output.insert(pos, str);
|
|
}
|
|
|
|
++arg_no;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline Composition &Composition::arg(char const * const cstr)
|
|
{
|
|
/* specialization to ensure that empty C strings show up
|
|
* in the output
|
|
*/
|
|
for (specification_map::const_iterator i = specs.lower_bound(arg_no),
|
|
end = specs.upper_bound(arg_no); i != end; ++i) {
|
|
output_list::iterator pos = i->second;
|
|
++pos;
|
|
|
|
output.insert(pos, std::string (cstr));
|
|
}
|
|
|
|
++arg_no;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline Composition::Composition(std::string fmt)
|
|
: arg_no(1)
|
|
{
|
|
std::string::size_type b = 0, i = 0;
|
|
|
|
// fill in output with the strings between the %1 %2 %3 etc. and
|
|
// fill in specs with the positions
|
|
while (i < fmt.length()) {
|
|
if (fmt[i] == '%' && i + 1 < fmt.length()) {
|
|
if (fmt[i + 1] == '%') { // catch %%
|
|
fmt.replace(i, 2, "%");
|
|
++i;
|
|
}
|
|
else if (is_number(fmt[i + 1])) { // aha! a spec!
|
|
// save string
|
|
output.push_back(fmt.substr(b, i - b));
|
|
|
|
int n = 1; // number of digits
|
|
int spec_no = 0;
|
|
|
|
do {
|
|
spec_no += char_to_int(fmt[i + n]);
|
|
spec_no *= 10;
|
|
++n;
|
|
} while (i + n < fmt.length() && is_number(fmt[i + n]));
|
|
|
|
spec_no /= 10;
|
|
output_list::iterator pos = output.end();
|
|
--pos; // safe since we have just inserted a string>
|
|
|
|
specs.insert(specification_map::value_type(spec_no, pos));
|
|
|
|
// jump over spec string
|
|
i += n;
|
|
b = i;
|
|
}
|
|
else
|
|
++i;
|
|
}
|
|
else
|
|
++i;
|
|
}
|
|
|
|
if (i - b > 0) { // add the rest of the string
|
|
output.push_back(fmt.substr(b, i - b));
|
|
}
|
|
}
|
|
|
|
inline std::string Composition::str() const
|
|
{
|
|
// assemble string
|
|
std::string str;
|
|
|
|
for (output_list::const_iterator i = output.begin(), end = output.end();
|
|
i != end; ++i)
|
|
str += *i;
|
|
|
|
return str;
|
|
}
|
|
}
|
|
|
|
// now for the real thing(s)
|
|
|
|
// a series of functions which accept a format string on the form "text %1
|
|
// more %2 less %3" and a number of templated parameters and spits out the
|
|
// composited string
|
|
template <typename T1>
|
|
inline std::string string_compose(const std::string &fmt, const T1 &o1)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2>
|
|
inline std::string string_compose(const std::string &fmt,
|
|
const T1 &o1, const T2 &o2)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3>
|
|
inline std::string string_compose(const std::string &fmt,
|
|
const T1 &o1, const T2 &o2, const T3 &o3)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4>
|
|
inline std::string string_compose(const std::string &fmt,
|
|
const T1 &o1, const T2 &o2, const T3 &o3,
|
|
const T4 &o4)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5>
|
|
inline std::string string_compose(const std::string &fmt,
|
|
const T1 &o1, const T2 &o2, const T3 &o3,
|
|
const T4 &o4, const T5 &o5)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6>
|
|
inline std::string string_compose(const std::string &fmt,
|
|
const T1 &o1, const T2 &o2, const T3 &o3,
|
|
const T4 &o4, const T5 &o5, const T6 &o6)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8, typename T9>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8, typename T9, typename T10>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
|
|
.arg(o10);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8, typename T9, typename T10,
|
|
typename T11>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
|
|
.arg(o10).arg(o11);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8, typename T9, typename T10,
|
|
typename T11, typename T12>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
|
|
.arg(o10).arg(o11).arg(o12);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8, typename T9, typename T10,
|
|
typename T11, typename T12, typename T13>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
|
|
.arg(o10).arg(o11).arg(o12).arg(o13);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8, typename T9, typename T10,
|
|
typename T11, typename T12, typename T13, typename T14>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
|
|
.arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
|
|
return c.str();
|
|
}
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5,
|
|
typename T6, typename T7, typename T8, typename T9, typename T10,
|
|
typename T11, typename T12, typename T13, typename T14,
|
|
typename T15>
|
|
inline std::string 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)
|
|
{
|
|
StringPrivate::Composition c(fmt);
|
|
c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
|
|
.arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
|
|
return c.str();
|
|
}
|
|
|
|
#endif // STRING_COMPOSE_H
|