Reputation: 349
I have the following code in C++ on Win32. It's simply a C++ warp on some Win32 API that returns a CHAR *
:
wstring expandEnvironmentVariables(const wstring & str)
{
DWORD neededSize = ExpandEnvironmentStrings(str.c_str(), nullptr, 0);
vector<WCHAR> expandedStr(neededSize);
if (0 == ExpandEnvironmentStrings(str.c_str(), expandedStr.data(), static_cast<DWORD>(expandedStr.size()))) {
return wstring(str);
}
return wstring(expandedStr.data());
}
What bothers me about this code, is the double copy of the result.
WCHAR
s.std::wstring
.Is there a way to implement this code with just a single copy, and without a major change to the signature of the function.
This is a specific example, but I'm more interested in the general solution and the right way to work with std::wstring
/std::string
, because this pattern shows itself in many places in the code.
Upvotes: 3
Views: 392
Reputation: 69892
wstring expandEnvironmentVariables(const wstring & str)
{
wstring expandedStr;
DWORD neededSize = ExpandEnvironmentStrings(str.c_str(),
nullptr, 0);
if (neededSize)
{
expandedStr.resize(neededSize);
if (0 == ExpandEnvironmentStrings(str.c_str(),
&expandedStr[0],
neededSize))
{
// pathological case requires a copy
expandedStr = str;
}
}
// RVO here
return expandedStr;
}
EDIT:
On reflection, since we're using c++ let's go the whole hog and put in proper error detection and report errors with an informative nested exception chain:
DWORD check_not_zero(DWORD retval, const char* context)
{
if(!retval)
throw std::system_error(GetLastError(),
std::system_category(),
context);
return retval;
}
std::wstring expandEnvironmentVariables(const std::wstring & str)
try
{
DWORD neededSize = check_not_zero(ExpandEnvironmentStrings(str.c_str(),
nullptr,
0),
"ExpandEnvironmentStrings1");
std::wstring expandedStr(neededSize, 0);
check_not_zero(ExpandEnvironmentStrings(str.c_str(),
&expandedStr[0],
neededSize),
"ExpandEnvironmentStrings2");
// RVO here
return expandedStr;
}
catch(...)
{
std::throw_with_nested(std::runtime_error("expandEnvironmentVariables() failed"));
}
Upvotes: 3
Reputation: 145289
Regarding the C++ side you can just use a wstring
directly as a result variable.
To get a pointer to the buffer of a wstring
of non-zero size, just use &s[0]
.
Just like std::vector
, std::basic_string
has a guaranteed contiguous buffer.
For the return it will probably get Return Value Optimization (RVO), and if not then it will be moved.
Disclaimer: I haven't checked the documentation of the API functions. I do not know if this code correct or even meaningful. I'm just assuming that.
Upvotes: 3