2

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 ?

ApprenticeHacker
  • 21,351
  • 27
  • 103
  • 153
  • What is the purpose of `reinterpret_cast` in this code, apart from summoning troubles? – n. m. could be an AI Jan 25 '12 at 11:30
  • Please note that `MessageBox` takes a **`LPCTSTR`**, which is a wide string or a narrow string depending on many factors. My guess is that if you want to be generic and safe, you have to copy the string somehow. You should decide for an internal representation (maybe UTF-8, so that you always use `char*` or `std::string`), and convert to the right Windows type at the `MessageBox` call site. Note that Windows will likely require you to use `wcout`, whereas on Linux, you should always go with `cout`. – Alexandre C. Jan 25 '12 at 11:48
  • You can test if `LPCTSTR` is made of wide characters by testing `#ifdef _UNICODE`. Unicode on Windows means (nowadays) UTF-16. – Alexandre C. Jan 25 '12 at 11:58
  • almost all suggestions are good and more or less working. I wonder which one should I accept? I'll wait a day or two to see which one gets the most upvotes. -_- – ApprenticeHacker Jan 26 '12 at 09:24

5 Answers5

5

You can use function overloading:

void print_error(char const* msg);
void print_error(std::string const& msg);
...
Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112
3

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 */
}
hmjd
  • 120,187
  • 20
  • 207
  • 252
2

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;
}
Alexander Vassilev
  • 1,399
  • 13
  • 23
1

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 */
}
obmarg
  • 9,369
  • 36
  • 59
  • True, but at the cost of performance when passing a plain char*, which would cause unnecessary copying of the string at least once, and generally the overhead of a stringstream when it is not always needed. This is a good generic function, but I think it should be specialized for char* and string – Alexander Vassilev Jan 26 '12 at 13:54
1

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;
}
Mr.Anubis
  • 5,132
  • 6
  • 29
  • 44