This is a side product of some evil plan to emit hundreds of fixed codepaths at compile time, for the sake of better performance for some simple ifs-fractals-renderer i hack at the moment (itself being an exercise in OpenMP, C++0x (as far as g++4.4 goes), Optimization, and patience of course).
So, what follows is a function that reminds of .net’s WriteLine()-family, which some say to be a typesafe variant of printf() (yes, the one dreaded for its ellipsis parameter, possibly opening the door to all kinds of vulnerabilities), and hence better. Of course there are iostreams in C++, but they have the disadvantage that you cannot flip arguments around during runtime, making them a non-option for internationalization purposes. You could also overload operators so as to produce typesafe variants of printf(). Anyways, I was snoopy about variadic templates in C++0x. And as it turned out, variadic templates are a bless!
#include <sstream> #include <string> #include <iostream> #include <stdexcept> // lambdas not yet template <typename T> std::string to_string (T val) { std::stringstream ss; ss << val; return ss.str(); } template <typename ...ARGS> void write (std::string const & fmt, ARGS... args) { const std::string argss[] = {to_string (args)...}; // <- indeed enum {argss_len = sizeof (argss) / sizeof(argss[0])}; // no range based for loops yet ("for (auto it : fmt)") for (auto it = fmt.begin(); it != fmt.end(); ++it) { if (*it == '{') { auto const left = ++it; for (; it != fmt.end(); ++it) { // closing brace: fine if (*it == '}') break; // check if numeric. if not, throw. switch (*it) { default: throw std::invalid_argument ( "syntax error in format string, " "only numeric digits allowed between " "braces" ); case '0':case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9':; }; } if (*it != '}') { throw std::invalid_argument ( "syntax error in format string, " "missing closing brace" ); } auto const right = it; if (left == right) { throw std::invalid_argument ( "syntax error in format string, " "no index given inside braces" ); } std::stringstream ss; ss << std::string(left,right); size_t index; ss >> index; if (index >= argss_len) { throw std::invalid_argument ( "syntax error in format string, " "index too big" ); } std::cout << argss[index]; } else { std::cout << *it; } } } void write (std::string const & str) { std::cout << str; } template <typename ...ARGS> void writeln (std::string const & fmt, ARGS... args) { write (fmt, args...); std::cout << '\n'; } void writeln (std::string const & str) { std::cout << str << '\n'; }
You can invoke this function like this (no ofuscation through operator overloading, full typesafety):
int main() { writeln ("Test: [{0},{1}]", 42, 3.14159); writeln ("Test: {1}/{0}!{0}?{0}!!", 43, "hello wurldz"); writeln ("Test: "); return 0; }
C++0x will also allow for user defined literals, allowing you to process strings (as in “strings”) at compile time, but it has not been scheduled for implementation in GCC as of time of writing.
I initially did some yacking about emitting hundreds of fixed codepaths for performance reasons. Have a look at the following example for a glimpse about how I will do it:
enum class Xtype { add, sub, yo, mama, end_ }; template <Xtype ...args> struct test; template <> struct test <> { static void exec () { std::cout << "-term-\n"; } }; template <Xtype ...others> struct test<Xtype::add, others...> { static void exec () { std::cout << "add\n"; test<others...>::exec(); } }; template <Xtype ...others> struct test<Xtype::sub, others...> { static void exec () { std::cout << "sub\n"; test<others...>::exec(); } }; template <Xtype ...others> struct test<Xtype::yo, others...> { static void exec () { std::cout << "yo\n"; test<others...>::exec(); } }; template <Xtype ...others> struct test<Xtype::mama, others...> { static void exec () { std::cout << "mama\n"; test<others...>::exec(); } }; int main() { test< Xtype::add, Xtype::add, Xtype::sub, Xtype::sub, Xtype::add, Xtype::add, Xtype::sub, Xtype::yo, Xtype::mama, Xtype::yo, Xtype::mama, Xtype::yo, Xtype::yo >::exec(); return 0; }
Output:
add
add
sub
sub
add
add
sub
yo
mama
yo
mama
yo
yo
-term-
No more need for clumsy nesting or for std::tuple (surely std::tuple itself largely profits from variadic templates; for the curious: the boost implementation of tuple is essentially a template with a plethora of parameters (http://www.boost.org/doc/libs/1_40_0/libs/tuple/doc/tuple_users_guide.html).
Btw, the status of implementation of C++0x in GCC is at http://gcc.gnu.org/projects/cxx0x.html, the implementation status of the updated standard library is at http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.200x.
If you are snoopy yourself, invoke g++ with -std=c++0x (all lowercase!).