Я пишу альтернативу sprintf()
с использованием рекурсивных вариационных шаблонов, как описано в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2087.pdf. Моя цель - простое добавление форматировщиков настраиваемых типов данных для пользовательских типов. Например, если базовая реализация выглядит следующим образом:Альтернативы частичной специализированности шаблонов для функций с рекурсивным вариационным шаблоном
#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");
}
тогда я мог бы добавить форматчик для моего класса Foo
(который, скажем, содержит метод customDescription()
, который возвращает wstring
), написав
template<typename... Args>
const wstring wsprintf<const Foo&>(const wchar_t *s, const Foo& foo, const Args&... args)
{
return wsprintf(s, foo.customDescription(), args...);
}
Я бы тогда быть в состоянии сделать это:
Foo bar;
wstring message = wsprintf("my foo tells me %s", bar);
Однако, как я написал этот код не будет работать, потому что часть ial шаблона для функций (PTSF) не допускается, как объясняется в http://www.gotw.ca/publications/mill17.htm.
Две альтернативы, как правило, доступны вместо PTSF являются:
- Исключите использование шаблонов в целом и использовать перегруженные функции.
- Создание статических классов для переноса специализированных реализаций функции.
Первый вариант не представляется возможным, так как рекурсивная VARIADIC шаблон подход к printf()
требует по меньшей мере, один шаблон аргумент (параметр VARIADIC пакета).
Когда я попытался реализовать второй вариант, я столкнулся с несколькими ошибками синтаксиса (встроенный в комментариях):
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
}
Я не знаю, как исправить эти ошибки. Есть идеи? Действительно ли я на правильном пути?