Нужна помощь с шаблонным программированием - PullRequest
1 голос
/ 25 января 2011

Я пытаюсь расширить свои навыки программирования шаблонов и столкнулся с проблемой, для которой я не вижу правильного решения.Это упражнение для личного обучения только для более сложных шаблонов.

Эта цель: написать шаблон для преобразования любого целочисленного типа (используя sprintf или swprintf) в строку или строку в зависимости от типа формата.sring.Нет необходимости в проверке ошибок (пока что).

Проблема в том, что формат указан как (const char*) NULL или (const wchar_t*) NULL

Мне нужно указать значение по умолчанию LITERALлибо "%i", либо L"%i", для этого мне нужно определить тип символа переменной формата.Я использую функции для этого сейчас, используя SFINAE.Однако я хотел бы использовать переменную для этого, но я не думаю, что SFINAY работает с переменными (или я ошибаюсь).

Вот мой (рабочий) код на данный момент:

////////////////////////////////////////////////////////////////////////////////

template < typename T ,typename I > 
inline 
typename std::enable_if< std::is_same< T ,char >::value ,int >::type 
str_printf ( T* szBuff ,int iLen ,const T* szFrmt ,I iNum )
{ return sprintf_s( szBuff ,iLen ,szFrmt ,iNum ); }

template < typename T ,typename I > 
inline 
typename std::enable_if< std::is_same< T ,wchar_t >::value ,int >::type 
str_printf ( T* szBuff ,int iLen ,const T* szFrmt ,I iNum )
{ return swprintf_s( szBuff ,iLen ,szFrmt ,iNum ); }

////////////////////////////////////////////////////////////////////////////////

template < typename T > 
inline 
typename std::enable_if< std::is_same< T ,char >::value ,const char* >::type 
Dflt_Frmt ()    { return "%i"; }

template < typename T > 
inline 
typename std::enable_if< std::is_same< T ,wchar_t >::value ,const wchar_t* >::type 
Dflt_Frmt ()    { return L"%i"; }

////////////////////////////////////////////////////////////////////////////////

template < typename T ,typename I > 
inline 
std::basic_string< T ,std::char_traits < T > > 
to_string ( I iNum ,const T* pszFrmt )
{
    const int iLen (65);
    T szBuff [iLen] = {0};

    std::basic_string< T ,std::char_traits < T > > frmt ((pszFrmt && (*pszFrmt)) ? pszFrmt : Dflt_Frmt<T>() );
    str_printf( szBuff ,iLen ,frmt.c_str() ,iNum );

    return szBuff;
}

////////////////////////////////////////////////////////////////////////////////

это то, что я хотел бы сделать (очевидно, это не работает)

////////////////////////////////////////////////////////////////////////////////

template < typename T ,typename I > 
inline 
std::basic_string< T ,std::char_traits < T > > 
to_string ( I iNum ,const T* pszFrmt )
{
    const int iLen (65);
    T szBuff [iLen] = {0};

    // declare a Variable of const T* and initialie it with "%i" or L"%i"
    typename std::enable_if< std::is_same< T ,char >::value      ,const char* >::type        dft("%i");
    typename std::enable_if< std::is_same< T ,wchar_t >::value   ,const wchar_t* >::type     dft (L"%i");
    // doesn't work (error : type is not a member of std::enable_if< ... > !

    std::basic_string< T ,std::char_traits < T > > frmt ((pszFrmt && (*pszFrmt)) ? pszFrmt : dft );

    str_printf( szBuff ,iLen ,frmt.c_str() ,iNum );

    return szBuff;
}

////////////////////////////////////////////////////////////////////////////////

Могу ли я сделать это аналогичным образом или рабочая версия - лучший?Или как это сделать>

Мне не нужно предлагать использовать строковые потоки (вопрос не в этом).

Использование MSVS 2010 (и извините, без повышения).

Спасибо.

Ответы [ 3 ]

2 голосов
/ 25 января 2011

Честно говоря, это единственное решение, которое я могу придумать:

template <typename T> struct Dft { static const T* value; };
template <> const char* Dft<char>::value = "%i";
template <> const wchar_t* Dft<wchar_t>::value = L"%i";

template < typename T ,typename I > 
inline 
std::basic_string< T ,std::char_traits < T > > 
to_string ( I iNum ,const T* pszFrmt )
{
    const int iLen (65);
    T szBuff [iLen] = {0};

    std::basic_string< T ,std::char_traits < T > > frmt ((pszFrmt && (*pszFrmt)) ? pszFrmt : Dft<T>::value );

    str_printf( szBuff ,iLen ,frmt.c_str() ,iNum );

    return szBuff;
};

Это не красиво, но работает.

1 голос
/ 25 января 2011

Использование вами enable_if во втором блоке кода превращается в серьезные ошибки, потому что вы не используете его в подписи шаблона.Вам может понадобиться что-то вроде boost::mpl::if_ для вычисления типа переменной dft;Я считаю, что вы можете просто привести от узкой строки к широкой, чтобы ваш формат работал в обоих случаях.

0 голосов
/ 25 января 2011

IMO, то, что вы пытаетесь сделать здесь, - довольно безнадежное начинание. Преобразование «% i» будет работать только с целыми числами, а не с (например) типами с плавающей запятой, поэтому ваш код работает, только если I равно int 1 . Для любого другого типа пользователь должен передать (правильную) строку формата, чтобы код имел определенное поведение. На данный момент я проигнорирую эту проблему и предположу, что пользователь передает (правильную) строку формата, если I не int.

Хотя вы можете захотеть расширить некоторые другие функции в будущем, сейчас у вас действительно есть только две возможности для типа строки формата: char * или wchar_t *. В таком случае кажется, что правильным способом справиться с вещами является простая перегрузка (или специализация, если вы настаиваете):

template <class T>
std::string to_string(T val, char *fmt = "%i") { 
    // for the moment using `sprintf`, simply because every knows it -- not really
    // advising its use in production code.
    char buffer[256];
    sprintf(buffer, fmt, val);
    return std::string(buffer);
}

template <class T>
std::wstring to_string(T val, wchar_t *fmt = L"%i") { 
    wchar_t buffer[256];
    wsprintf(buffer, fmt, val);
    return std::wstring(buffer);
}

Прямо сейчас вы делаете «переключение типа», чего почти всегда можно избежать (и, как правило, лучше избегать) в C ++. Незначительная деталь, которую вы делаете (или пытаетесь в любом случае) во время компиляции, а не во время выполнения, на самом деле не меняет этого.

1 Ну, вы могли бы иметь возможность утверждать, что это должно работать, если I равно unsigned int и значение находится в пределах диапазона, который может быть представлен как int, но это лучшее, на что вы можете надеяться (и даже это весьма сомнительно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...