15

I have an LPCTSTR and want to call a function that takes a std::string parameter.

What conversion do I need to do?

7 Answers 7

21

Tip of the iceberg

LPCTSTR can be either a single-byte or a multibyte string (depends on the UNICODE constant being defined during compilation or not), while std::string's users (including your function) normally use it to hold a single-byte string.

You 'd need two conversions: one for LPCSTR (non-UNICODE build) and one for LPCWSTR (UNICODE build). The first one is simple:

std::string convert(LPCSTR str) {
    return std::string(str);
}

The second one needs its input parameter to be converted to another encoding first with WideCharToMultiByte. Do not be alarmed by the name, the result can be a single-byte char string; that depends on the CodePage parameter. You will have to use a codepage for a single-byte encoding, such as CP_ACP.

Update: WideCharToMultiByte example

Be aware that accurately converting to a single-byte encoding is technically impossible if the input string contains characters not existing in the target encoding's code page. Since you mention it's going to be for filesystem functions, if the file path contains such characters the conversion will not be 100% accurate and the subsequent function calls will fail.

std::string MBFromW(LPCWSTR pwsz, UINT cp) {
    int cch = WideCharToMultiByte(cp, 0, pwsz, -1, 0, 0, NULL, NULL);

    char* psz = new char[cch];

    WideCharToMultiByte(cp, 0, pwsz, -1, psz, cch, NULL, NULL);

    std::string st(psz);
    delete[] psz;

   return st;
}

Caveat emptor: The example above is from some code I had lying around and is not production-grade quality. The one immediately obvious flaw is that it is not exception-safe. It might also kill all the nice purple unicorns. Use it only as an example.

The full encoding hell

The naked truth is that std::string can be used for multibyte encodings (such as UTF8) just fine -- you can even use it to hold wide-char strings, since it's just a binary-safe array of bytes at heart.

The problem is that the STL functions that apply to std::string expect its contents to be in a single-byte encoding, and they won't produce correct results if this is not true.

By extension, we don't know what your function that takes an std::string parameter expects -- it might expect a string encoded in UTF-8. But "by convention", I 'm assuming it also wants a single-byte-encoded string.

Sign up to request clarification or add additional context in comments.

11 Comments

Thanks! Yes, it is a UNICODE build (with #define UNICODE set). The function I am calling will take the std::string and use it as the filename when calling std::fstream::open() - so looks like WideCharToMultiByte is the way to go. That's a lot of parameters though - any chance of extending your answer to include an example of calling that function?
@GrahamS: Sure. I grepped and found some relevant old code of mine.
@GrahamS: Please be aware of all the pitfalls. The STL does not provide support for non-ANSI file paths AFAIK, so you may hit some hard obstacles here. It's even possible that you will be forced to abandon the STL.
@Jon: Actually, that's a Microsoft extension. And if you're already using LPCTSTR, you're not restricting yourself further by using fstream::fstream(const wchar_t *filename). In fact, with that overload, it could be said that fstream::fstream already takes an LPCTSTR
@MSalters: I 've never seen LPCTSTR outside a Windows context, so I assumed WideCharToMultiByte is not a problem -- unless you are referring to something else as a MS extension? Also, the answer addresses the original question (no mention of fstream).
|
17

in one line:

std::string s = CT2A( lpctstr );

Comments

2

Pragmatic approach:

     LPCTSTR input;

     std::string s;

#ifdef UNICODE
     std::wstring w;
     w = input;
     s = std::string(w.begin(), w.end()); // magic here
#else
     s = input;
#endif

See other answers for excellent backgrounders!

3 Comments

error C2679: binary '=' : no operator found which takes a right-hand operand of type 'std::wstring' (or there is no acceptable conversion) at the s = w line, do I need more magic?
This "works" in the US and Western Europe (ACP = latin-1). It breaks in interesting ways everywhere else.
Agreed. See heading "Pragmatic Approach" and disclaimer "See other answers for excellent backgrounders!" (note exclamation point). I voted your comment, so it will be extra visible
2

From your comment: "The function I am calling will take the std::string and use it as the filename when calling std::fstream::open()"

Well, that's wrong. The function really should take a tstring (defined as typedef std::basic_string<TCHAR> tstring). There are many Windows files whose name cannot be represented without Unicode. For instance, all files under \User\<myusername\My Documents\ if <myUserName> contains a non-ANSI character (and you really don't want to tell your user that he has a bad name!)

But once you've changed to tstring, it just works. You still get the same std::fstream object.

1 Comment

+1 A fair point. Sadly I'm not responsible for the library that function is in so I can't change it, but I will raise that point with the author though, thanks.
1

LPCTSTR is a Windows define that reads as something like 'long pointer to a const character-type string.' I'm not actually sure what the T stands for, but it has to do with the project's character set.

If your project is using the Unicode character set, this type is a const wchar_t*, which uses two bytes per character. If your project is using the multi-byte characters character set, this type is a const char*, which uses one byte per character.

Most likely your project's character set is Unicode, so LPCTSTR is a const wchar_t*. Since std::string uses one byte per character, it can't hold this two byte per character string. std::wstring can, however.

If you need to convert the const wchar_t* to a const char* to allow it to be assigned to a string, you can use functions like wcstombs to do this. If you're including ATL (specifically atlconv.h) in your project, it provides macros to do this more easily:

USES_CONVERSION;
const wchar_t* = L"Wide string";
std::string str = W2A(value);

Comments

1

This snippet of code should do conversion from LPWSTR to char*/std::string

LPWSTR input = L"whatever";
int cSize = WideCharToMultiByte (CP_ACP, 0, input, wcslen(input), NULL, 0, NULL, NULL);
std::string output(static_cast<size_t>(cSize), '\0');
WideCharToMultiByte (CP_ACP, 0, input, wcslen(input),
                       reinterpret_cast<char*>(&output[0]), cSize, NULL, NULL);

and this snippet should do conversion from single-byte std::string to LPWSTR

std::string input = "whatever";
//computing wchar size:
int wSize = MultiByteToWideChar (CP_ACP, 0, (LPCSTR) input.c_str (), -1, 0, 0);

//allocating memory for wchar:
LPWSTR output = new WCHAR[wSize];

//conversion from string to LPWSTR:
MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED , (LPCSTR) input.c_str (), -1, output, wSize);

2 Comments

I'm going the other way though: LPCTSTR to std::string
sorry I just realized that; i am looking the other way conversion snippet
0

I don't know if this solution might help you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.