If you are using Microsoft Visual Studio the use of the Microsoft Foundation Clases (MFC) is often seen as a must. But MFC is a relative large library and many modules don’t use much of it. There are cases where the only piece of MFC is CString. But linking against MFC just for CString is superfluous.
Since the standard C++ library is always bundled with every compiled program the use of std::string and std::wstring is a good alternative. Conversion from and to CString is trivial, if you know the current settings.
The problem is that many functions defined by the WinAPI and MFC are dependent if Unicode is used or not. Depending on the settings either char or wchar_t us used. So code is independent to this setting TCHAR can be used. It will then be defined to the one or the other.
In the one case you can plainly convert to std::string in the other std::wstring. But which do you use in your code. This gets worse if you receive and return the strings; character set conversion is out of the question.
If you read about this in the Microsoft Developer Network (MSDN) you get a plain solution: Use the preprocessor.
#ifdef _UNICODE
#define tstring wstring
#else
#define tstring string
#endif
This solution follows the same as many proposed by Microsoft. But if you give me a Euro for every #undef I needed to write in my code, bacause a macro from the WinAPI tainted my code, I would be rich.
Since we are programming C++, it is wise to solve the problem with the means of the language instead of the using a preprocessor hack. The definition of tstring follows the definition of string and wstring. tstring is a variant of the template basic_string with TCHAR. To add a little type safety and portability I defined tchar_t following the idea of wchar_t.
namespace std
{
typedef TCHAR tchar_t;
typedef basic_string<tchar_t> tstring;
}
tchar_t and tstring where defined in the namespace std, to show that it actually just is a special form of basic_string and fully compatible to other standard code.
Well now that you have your string class dependent on TCHAR, there is the problem of streams. Streams are also parameterized for char or wchar_t. But here the solution is straight forward. Define your own types based on ”basic_streamtype”
namespace std
{
typedef basic_ios<tchar_t> tios;
typedef basic_streambuf<tchar_t> tstreambuf;
typedef basic_istream<tchar_t> tistream;
typedef basic_ostream<tchar_t> tostream;
typedef basic_iostream<tchar_t> tiostream;
typedef basic_stringbuf<tchar_t> tstringbuf;
typedef basic_istringstream<tchar_t> tistringstream;
typedef basic_ostringstream<tchar_t> tostringstream;
typedef basic_stringstream<tchar_t> tstringstream;
typedef basic_filebuf<tchar_t> tfilebuf;
typedef basic_ifstream<tchar_t> tifstream;
typedef basic_ofstream<tchar_t> tofstream;
typedef basic_fstream<tchar_t> tfstream;
}
Conversion to and from CString, LPTCSTR or LPTSTR is plain simple. The most important aspect is that no character conversion is necessary.
Conversion from CString to std::tstring:
CString cstr(_T("test"));
std::string std_str(cstr);
Conversion from std::tstring to CString:
std::string std_str(_T("test"));
CString cstr(std_str.c_str());
Just because other modules use TCHAR, LPTSTR, LPTCSTR or CString does not mean you can’t use C++ standard classes. You only have to create your own variants.