2

I'm curious if the following code is correct? I'm running it on a somewhat older version of VS 2008, for a Windows-only C++ project.

My goal is to preallocate memory in std::string to pass it into a WinAPI knowing the required size in characters:

//'hWnd' = window handle int nLn = GetWindowTextLength(hWnd); //Text variable to collect text in std::wstring str; str.reserve(nLn); GetWindowText(hWnd, (LPTSTR)str.data(), nLn); 

My concern here is that str.data() returns const wchar_t * and GetWindowText() requests LPTSTR, which is not a const buffer. Would a type cast be OK there?

3
  • 1
    If you cannot call GetWindowTextW (vs. GetWindowText), you should use typedef std::basic_string<TCHAR> tstring; as well. Otherwise you have mismatching character types. Likewise, when using a std::vector, it should be std::vector<TCHAR> as well. Or just drop MBCS support and call the Unicode variants of the Windows API. Commented Dec 16, 2015 at 1:38
  • I've asked a very-much-related question stackoverflow.com/questions/17601632/… My conclusion was that it was easier to live with an extraneous copy. Commented Dec 16, 2015 at 3:43
  • @IInspectable: Good point. I overlooked it. Thank you. Commented Dec 16, 2015 at 4:06

2 Answers 2

3

I can't speak for VS2008, but most pre-C++11 follow the C++11 rule of &str[0] + i == str.data() + i, so &str[0] works and doesn't require any casts1, which is a much safer route than subverting the type system and the standard. Just be careful because you aren't permitted to overwrite the null terminator after the string, even with another null terminator. If you aren't able to guarantee this for VS2008, then a std::vector<wchar_t> would be the preferred solution.

However, you have another problem. reserve doesn't stop it being undefined behaviour to access str out of bounds. Bounds are based on size(), not capacity(). You need to change reserve to resize.

Even then, you have another problem. GetWindowTextLength doesn't include the null terminator. You must use nLn + 1 in the code following that call.

With the changes made, this will work with any conforming C++11 implementation, including some pre-C++11 implementations, and ignoring error checking:

int nLnWithNul = GetWindowTextLength(hWnd); std::wstring str(nLnWithNul, '\0'); // bit of a shorthand for the resize call int nCopiedLn = GetWindowText(hWnd, &str[0], nLnWithNul); str.resize(nCopiedLn); 

The last resize call handles the copied title being shorter than nLnWithNul. For example, if the title shrinks since GetWindowTextLength was called. After resizing, the string will contain only the copied text, not including any NUL characters after it.


1: See the answer to my previous question.

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

11 Comments

You didn't say explicitly, but (LPTSTR)str.data() would lead to undefined behaviour. The standard says directly that trying to write via data() causes UB. &str[0] must be used instead.
C++ has always required contiguous storage of the controlled sequence. This wasn't explicitly spelled out, but c_str() is required to return in O(1). This leaves only one way to store the controlled sequence. Regardless of the implementation details, you are recommending to rely on undefined behavior. That's not at all prudent.
@IInspectable please provide a reference to the C++03 standard specifying O(1) requirement for c_str(). Also, if your claim is correct (i.e. contiguous storage was always required) then chris's is not recommending to rely on UB
@IInspectable that website is not the C++03 standard . As far as I can see from reading standard drafts , that requirement was added in C++11.
@chris you're right, but the text could conceivably change between the two calls.
|
0

No, you cannot do that according to the C++ standard. But you can do it if you use std::vector<wchar_t> instead of std::wstring:

int nLn = GetWindowTextLength(hWnd); std::vector<TCHAR> str(nLn); // size, not reserve() int size = GetWindowText(hWnd, str.data(), nLn); str.resize(size); // will make empty in case of error 

See also: writing directly to std::string internal buffers

3 Comments

Care to elaborate? C++11 introduced guarantees for std::string, and I fail to see any violations.
@IInspectable OP specified Visual Studio 2008. I don't think that supports C++11, hence my answer does not consider C++11 (and would be totally different if it did).
Pre-C++11, I'm not aware of any rule, that prohibits writing the array at location &str[0]. Is there?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.