I am trying to expand my template programming skills and I am facing a problem to which I don't see the right solution for. This is a personal training execise only to do some more advanced templating.
This the goal : write a template to convert any integer type (using sprintf or swprintf) to either string or wstring depending on the type of the format sring. There is no need for error-checking (for now anuway).
The problem is when an format is specified as (const char*) NULL
or (const wchar_t*) NULL
I need to supply a default LITERAL value as either "%i"
or L"%i"
an for that I need to determine the char-type of the format-variable.
I am using a functions for that now ,using SFINAE.
However I would like to use a variable for that ,but I don't think SFINAY works on varaiables (or am i wrong).
Here is my (working) code so far:
////////////////////////////////////////////////////////////////////////////////
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开发者_Python百科::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;
}
////////////////////////////////////////////////////////////////////////////////
this this what i would like to do (obviously it's not workin)
////////////////////////////////////////////////////////////////////////////////
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;
}
////////////////////////////////////////////////////////////////////////////////
Can I do this in a simillar way or is the working version the best way ? Or how to do this >
I don't need suggestion to use stringstreams (that's not what this question is about).
Using MSVS 2010 (and sorry ,no boost).
Thank you.
Frankly, this is the only solution I can think of:
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;
};
It's not pretty, but it works.
Your uses of enable_if
in your second code block turn into hard errors because you are not using it in the signature of a template. You might need something like boost::mpl::if_
to compute the type of the variable dft
; I believe you can just cast from a narrow string to a wide one to get your format to work in both cases.
IMO, what you're trying to do here is a pretty hopeless endeavor. A "%i" conversion will only work with integers, not (for example) floating point types, so your code only works if I
is int
1. For any other type, the user must pass a (correct) format string for the code to have defined behavior. For the moment, I'll ignore this issue, and just assume the user passes a (correct) format string if I
isn't int
.
While you might want to expand to a few other things sometime in the future, for now you really only have two possibilities for the type of the format string: char *
or wchar_t *
. That being the case, it seems like the right way to handle things is a simple overload (or specialization, if you insist):
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);
}
Right now, you're doing essentially a "switch on type", something that's almost always avoidable (and generally best avoided) in C++. The minor detail that you're doing it (or trying to anyway) at compile-time instead of run-time doesn't really change that.
1Well, you might be able to argue that it should work if I
is unsigned int
and the value is within the range that can be represented as an int
, but that's about the best you can hope for (and even that's highly questionable).
精彩评论