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>