Reputation: 22327
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?
Upvotes: 2
Views: 921
Reputation: 61970
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.
Upvotes: 3
Reputation: 249642
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
Upvotes: 0