How to use Templates when working with std::strings and c-style strings?

585 views Asked by At

I was just messing around with templates, when I tried to do this:

template<typename T> void print_error(T msg)
{
#ifdef PLATFORM_WIN32
    ::MessageBox(0, reinterpret_cast< LPCSTR >(msg), "Error", MB_ICONERROR|MB_OK);
#else
    cout << msg << endl;
#endif /* PLATFORM_WIN32 */
}

Of course this obviously will not work if you pass an std::string as T. Because a string can't be cast to a char*, But can this function be coded in a way that it will allow me to pass both a c-style char* array and a c++ std::string as the parameters, and convert them to LPCSTR ?

5

There are 5 answers

2
Andriy Tylychko On BEST ANSWER

You can use function overloading:

void print_error(char const* msg);
void print_error(std::string const& msg);
...
1
hmjd On

This would work:

#include <sstream>

template<typename T> void print_error(T msg)
{
    std::ostringstream s;
    s << msg;

#ifdef PLATFORM_WIN32
    ::MessageBox(0, s.str().c_str(), "Error", MB_ICONERROR|MB_OK);
#else
    cout << s.str() << endl;
#endif /* PLATFORM_WIN32 */
}
0
Alexander Vassilev On

There are several ways to achieve this. One is to combine templated functions with function overloading:

template<typename T>
void print_error(T msg)
{
   ::MessageBox(0, reinterpret_cast< LPCSTR >(msg), "Error", MB_ICONERROR|MB_OK);
   cout << msg << endl;
}

void print_error(const std::string& msg)
{
   ::MessageBox(0, reinterpret_cast< LPCSTR >(msg.c_str()), "Error", MB_ICONERROR|MB_OK);
   cout << msg << endl;
}

int main()
{
string test = "test";
print_error(test);
print_error("test");
return 0;
}

Another way is to partially specialize a class template (function templates cannot be partially specialized) to handle a tagged template argument which tells it that the value is an std::string:

template <typename T>
class StringArgument{};

template <typename T>
struct ErrorPrinter
{
static void print_error(T msg)
{
   ::MessageBox(0, reinterpret_cast< LPCSTR >(msg), "Error", MB_ICONERROR|MB_OK);
}
};

template <typename T>
struct ErrorPrinter<StringArgument<T> >
{
static void print_error(T msg)
{
   ::MessageBox(0, reinterpret_cast< LPCSTR >(msg.c_str()), "Error", MB_ICONERROR|MB_OK);
}
};

int main()
{
string test = "test";   
ErrorPrinter<const char*>::print_error("sdfsdfsdf");
ErrorPrinter<StringArgument<string> >::print_error(test);

return 0;
}
0
Mr.Anubis On

You can enable print_error if T is char* else it will be compile time error i.e (you need include type_traits and c++11):

template<typename T> 
typename std::enable_if< std::is_same<T, char*>::value, void>::type print_error(T msg)
{
    ::MessageBox(0, reinterpret_cast< LPCSTR >(msg), "Error", MB_ICONERROR|MB_OK);
    cout << msg << endl;
}
1
obmarg On

To elaborate slightly on the solution offered by hmjd this solution should work with any string input, and also integers and the like. It should also work with unicode activated on windows.

#include <sstream>

template<typename T> void print_error(T msg)
{
#ifdef PLATFORM_WIN32
    std::basic_ostringstream< TCHAR > ss;
    ss << msg;
    ::MessageBox(0, ss.str().c_str(), "Error", MB_ICONERROR|MB_OK);
#else
    cout << msg << endl;
#endif /* PLATFORM_WIN32 */
}