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.
// (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;
}
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>