I have been working now on a project based on DirectX and I am struggling with something. Though the DirectX SDK relies heavily on Windows data types like LPSTR, LPCSTR, ... I have been using for my own classes std::string which was quite easy to work with, but it looks strange to mix both ways of handling strings. Although this is a vague question, what do you suggest - should I work with the strings or with the Windows pointers for different string types to keep some consistency?
3 Answers
LPSTR is just an alias for char* and LCPSTR is an alias for const char*, so your question actually sounds like "Should I use C++ strings or C strings?"
Well, C++ std::string has a member function called c_str() (or the equivalent data() function for STL compatibility) that returns a (non-modifiable) C string. So any time a function accepts an LPCTSTR, you can provide the output of c_str() as an argument.
I suggest you working with C++ std::string whenever you can, it is safer.
7 Comments
.data() or &str[0] to get char*.data() and c_str() is UB though. Also, to the best of my knowledge, they both return const char*&str[0] is what I'm used to. i.e. char* str2 = &str[0];. I admit I'm not sure if data() returns a const char* or a char*.&str[0] to modify the buffer as long as you don't bump into the null terminator, and data() and c_str() are equivalent. Also note that all of this ease only comes with C++11, where string buffers have to have contiguous characters.In modern C++ code, I would just use a robust string class like std::string, or better std::wstring for Unicode UTF-16 support with Windows. Then, you can convert to "raw" C strings at the API boundaries.
To get a read-only "raw" C string pointer, you can use std::[w]string::c_str() method.
Instead, if you want to modify std::[w]string buffer, you can first call the .resize() method to prepare the string buffer, making it big enough; then you can use &str[0] to access the internal buffer for those APIs than need to write into it.
Note that on Windows specific code, ATL's CString is another convenient string class you may want to use. It has some platform-specific convenient facilities, like loading string from resources (which is good for app localization).
Since CString offers an implicit conversion to LPCTSTR, you can just pass CString instances to APIs expecting a raw LPCTSTR. Instead, if you need to modify the internal CString buffer, you can use GetBuffer()/ReleaseBuffer() methods.
Comments
Building on @Andy Prowl's answer, you might want to code everything using "T" strings (i.e. LPTSTR instead of LPSTR or LPWSTR). The exact type of T (really TCHAR) is set at compile time based on whether you have UNICODE and _UNICODE #defined. This gives you compatibility with both.
You can still use C++ strings with this technique by deriving a tstring class from basic_string<>:
typedef std::basic_string<TCHAR> tstring;
4 Comments
std::wstring and wchar_t (or CString in Unicode builds, corresponding to CStringW), instead of the old ANSI/MBCS-compatible TCHAR model. I consider the TCHAR model something obsolete and useless in modern days.Passed them by doesn't make sense because those newer technologies are built on top of the older ones. They are still in great use today. However, TCHAR has little use other than for old code bases or backwards compatibility.
std::stringis by far easier to work with, and access to the underlying C string is only a few extra characters. That said, it's a bit more of a bother to use to interface with C in C++03 than it is in C++11.std::wstringon windows. See std::wstring VS std::string.