C++11 Variadic Templates: Format("I %%% about %%% of %%%","dream", 7,
9) == "I dream about 7 of 9"
I wanted to write a function that takes a string containing a number
of placeholders and a corresponding number of other parameters
(perhaps non-pod) and returns a string with the placeholders replaced
with stringified versions of the parameters:
For example:
Format("I %%% about %%% of %%%","dream", 7, 9)
....would evaluate to...
"I dream about 7 of 9"
....in a similar way to what...
ostringstream os;
os << "I " << "dream" << " about " << 7 << " of " << 9;
return os.str();
....would do.
I'm using gcc 4.6 in -std=c++0x mode so Variadic Templates are
available.
I haven't used Variadic Templates before. My first draft is below. It
appears to work, but it's using a linear recursion. My question is,
is there a way to write it iteratively?
Basically in my recursive solution I define...
Format(s):
output(s)
....as the base case and then...
Format(s, head, tail...):
divide s into three substrings: (eg "foo %%% bar %%% baz")
1. before_first_placeholder (eg "foo ")
2. first_placeholder (eg "%%%")
3. after_first_placeholder (eg " bar %%% baz")
output(before_first_placeholder)
output(head)
output(Format(after_first_placeholder, tail))
....as the recursive case.
Working code and a test case is below. Feedback/thoughts appreciated.
============================ CUT HERE
==========================================
// (C) 2011, Andrew Tomazos <http://www.tomazos.com>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
class StringFormatException {};
const string g_sPlaceholder("%%%");
template <class ... T>
inline string Format(const string& sFormat, const T& ...);
template <class ... T>
inline string Format(const string& sFormat)
{
size_t iFirstPlaceholderPos = sFormat.find(g_sPlaceholder);
if (iFirstPlaceholderPos != string::npos)
throw StringFormatException();
return sFormat;
}
template <class Head, class ... Tail>
inline string Format(const string& sFormat, const Head& head, const
Tail& ... tail)
{
stringstream os;
size_t iFirstPlaceholderPos = sFormat.find(g_sPlaceholder);
if (iFirstPlaceholderPos == string::npos)
throw StringFormatException();
else
{
string sFront(sFormat, 0, iFirstPlaceholderPos);
string sBack(sFormat, iFirstPlaceholderPos +
g_sPlaceholder.size());
os << sFront << head << Format(sBack, tail ... );
}
return os.str();
}
int main()
{
if (Format("I %%% about %%% of %%%","dream", 7, 9) == "I dream
about 7 of 9")
cout << "pass" << endl;
else
cout << "fail" << endl;
return 0;
}
============================ CUT HERE
==========================================
Tested as follows:
$ cat > test.cpp
<paste above>
$ g++ --version
g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
$ g++ -std=c++0x test.cpp
$ a.out
pass
Enjoy,
Andrew.
--
Andrew Tomazos <andrew@tomazos.com> <http://www.tomazos.com>