I’m writing an alternative to sprintf() using recursive variadic templates, as explained in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2087.pdf. My goal is to allow easy addition of custom data type formatters for user-defined types. For example, if the basic implementation looks like this:
#include <iostream>
#include <sstream>
#include <wchar.h>
#include <stdexcept>
using std::wstring;
using std::wstringstream;
const wstring wsprintf(const wchar_t *s)
{
wstringstream outstream;
while(*s)
{
if (*s == L'%' && *++s != L'%')
throw std::runtime_error("invalid format string: missing arguments");
outstream << *s++;
}
return outstream.str();
}
template<typename T, typename... Args>
const wstring wsprintf(const wchar_t *s, const T& value, const Args&... args)
{
wstringstream outstream;
while(*s)
{
if(*s == L'%' && *++s != L'%')
{
outstream << value << wsprintf(++s, args...);
return outstream.str();
}
outstream << *s++;
}
throw std::runtime_error("extra arguments provided to wsprintf");
}
then I could add a formatter for my class Foo (which, let’s say, contains the method customDescription() that returns a wstring) by writing
template<typename... Args>
const wstring wsprintf<const Foo&>(const wchar_t *s, const Foo& foo, const Args&... args)
{
return wsprintf(s, foo.customDescription(), args...);
}
I would then be able to do this:
Foo bar;
wstring message = wsprintf("my foo tells me %s", bar);
However, the way I’ve written this code will not work because partial template specialization for functions (PTSF) is not allowed, as explained in http://www.gotw.ca/publications/mill17.htm.
The two alternatives generally available in lieu of PTSF are:
- Eliminate the use of templates altogether and use overloaded functions.
- Create static classes to wrap specialized implementations of the function.
The first alternative doesn’t seem feasible because the recursive variadic template approach to printf() requires at least one template argument (the variadic parameter pack).
When I tried to implement the second alternative, I ran into several syntax errors (inline as comments):
namespace wsprintf_impl {
struct wsprintf
{
static const wstring impl(const wchar_t *s)
{
wstringstream outstream;
while(*s)
{
if (*s == L'%' && *++s != L'%')
throw std::runtime_error("invalid format string: missing arguments");
outstream << *s++;
}
return outstream.str();
}
};
// ERROR: redefinition of 'wsprintf' as different kind of symbol
template< class T, class Args&... args >
struct wsprintf
{
static const wstring impl(const wchar_t *s, const T& value, const Args&... args)
{
wstringstream outstream;
while(*s)
{
if(*s == L'%' && *++s != L'%')
{
outstream << value << wsprintf::impl(++s, args...);
return outstream.str();
}
outstream << *s++;
}
throw std::runtime_error("extra arguments provided to wsprintf");
}
};
}
template< class T, class Args&... args >
wstring wsprintf(const wchar_t *s, const T& value, const Args&... args)
// ERROR: type 'const Args &' of function parameter pack does not contain any unexpanded parameter packs
// ERROR: declaration of 'args' shadows template parameter
{
return wsprintf_impl::wsprintf<T, args...>::impl(s, value, args...);
// ERROR: expected '>'
// ERROR: expected '(' for function-style cast or type construction
}
I’m not sure how to fix these errors. Any ideas? Am I on the right path in the first place?
The problem is that
wsprintfis declared as both a class and a class template. Just make it be a class template, and the first case is the specialization for no arguments: