需要一些模板编程方面的帮助

时间:2011-01-25 03:23:14

标签: c++ templates

我正在尝试扩展我的模板编程技能,我正面临一个问题,我没有看到正确的解决方案。 这是个人训练,只是为了做一些更高级的模板。

这个目标:编写一个模板,将任何整数类型(使用sprintf或swprintf)转换为字符串或wstring,具体取决于格式的类型。 不需要进行错误检查(现在已经开始了)。

问题在于格式指定为(const char*) NULL(const wchar_t*) NULL

我需要提供默认的LITERAL值"%i"L"%i" 为此,我需要确定格式变量的char类型。 我现在正在使用SFINAE的功能。 但是我想使用一个变量,但我不认为SFINAY在varaiables上工作(或者我错了)。

到目前为止,这是我的(工作)代码:

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

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;
}

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

我可以用类似的方式做到这一点,还是最好的工作版本? 或者如何执行此操作&gt;

我不需要建议使用stringstreams(这不是这个问题的内容)。

使用MSVS 2010(抱歉,没有提升)。

谢谢。

3 个答案:

答案 0 :(得分:2)

坦率地说,这是我能想到的唯一解决方案:

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 :(得分:1)

您在第二个代码块中使用enable_if会变成硬错误,因为您没有在模板的签名中使用它。您可能需要boost::mpl::if_之类的内容来计算变量dft的类型;我相信你可以从一个窄的字符串转换成一个宽的字符串,以使你的格式在两种情况下都能正常工作。

答案 2 :(得分:0)

IMO,你在这里想要做的是一个相当无望的努力。 “%i”转换仅适用于整数,而不适用于(例如)浮点类型,因此只有Iint 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 好吧,如果Iunsigned int并且值在范围内,可能能够证明它应该有效可以表示为int,但这是你所希望的最好的(甚至是非常值得怀疑的)。