How do I get a writable C buffer from a std :: string?
I'm trying to convey your code using MFC CString
in std::string
the Microsoft Windows platform. And I'm interested in something. Let's say in the following example:
CString MakeLowerString(LPCTSTR pStr)
{
CString strLower = pStr ? pStr : L"";
CharLower(strLower.GetBuffer()); //Use WinAPI
strLower.ReleaseBuffer();
return strLower;
}
I am using strLower. GetBuffer () to get a writeable buffer that will be passed to CharLower . But I don't see a similar method in std::string
.
Am I missing something? And if so, how would you overwrite the method above using std::string
?
source to share
We don't use MFC in my new job, but luckily std lib and C ++ 11, so I ended up with the same question as c00000fd. Thanks to BitTickler's answer, I came up with the idea of ββusing an internal internal buffer for Win32-API via &s[0]
resp. &s.front()
catch.
Using a Win32-API function that shrinks the internal string buffer
Suppose you have a string that will be truncated using a Win32-API function. ::PathRemoveFileSpec(path)
- you can follow this approach:
std::string path( R("?(C:\TESTING\toBeCutOff)?") );
::PathRemoveFileSpec( &path.front() ); // Using the Win32-API
// and the the string internal buffer
path.resize( strlen( path.data() ) ); // adjust the string length
// to the first \0 character
path.shrink_to_fit(); // optional to adjust the string's
// capacity - useful if you
// do not plan to modify the string again
Unicode version:
std::wstring path( LR("?(C:\TESTING\toBeCutOff)?") );
::PathRemoveFileSpec( &path.front() ); // Using the Win32-API
// and the the string internal buffer
path.resize( wcslen( path.data() ) ); // adjust the string length
// to the first \0 character
path.shrink_to_fit(); // optional to adjust the string's
// capacity - useful if you
// do not plan to modify the string again
Using a Win32-API function that expands the internal string buffer
Assuming you have a string that needs to be expanded or populated with a Win32-API function. ::GetModuleFileName(NULL, path, cPath)
to get your executable path - you can follow this approach:
std::string path;
path.resize(MAX_PATH); // adjust the internal buffer size
// to the expected (max) size of the
// output-buffer of the Win32-API function
::GetModuleFileName( NULL, &path.front(), static_cast<DWORD>( path.size() ) );
// Using the Win32-API
// and the the string internal buffer
path.resize( strlen( path.data() ) ); // adjust the string length
// to the first \0 character
path.shrink_to_fit(); // optional to adjust the string's
// capacity - useful if you
// do not plan to modify the string again
Unicode version:
std::wstring path;
path.resize(MAX_PATH); // adjust the internal buffer size
// to the expected (max) size of the
// output-buffer of the Win32-API function
::GetModuleFileName( NULL, &path.front(), static_cast<DWORD>( path.size() ) );
// Using the Win32-API
// and the the string internal buffer
path.resize( wcslen( path.data() ) ); // adjust the string length
// to the first \0 character
path.shrink_to_fit(); // optional to adjust the string's
// capacity - useful if you
// do not plan to modify the string again
When you finally shrink to fit the string, you will need one more line of code when expanding the internal string buffer compared to the MFC alternative, it has almost the same overhead when shrinking the string.
The advantage of the approach std::string
as opposed to the approach CString
is that you don't need to declare an additional C-String pointer variable, you just work with the official methods std::string
and with a single strlen
/ wcslen
. My approach shown above only works for the shortening variant when the resulting Win32-API buffer ends at zero, but for this special case where the Win32-API returns an inexhaustible string then - like the method CString::ReleaseBuffer
- you must explicitly know and specify the new string length / buffer on path.resize( newLength )
- exactly the same as path.ReleaseBuffer( newLength )
for the alternative CString
.
source to share
The accepted lowercase way std::string
is:
#include <algorithm>
#include <string>
std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(), ::tolower);
You really can't get around iterating through each character. The initial Windows API call will iterate over characters internally.
If you need to get toLower()
for a language version other than the standard "C" locale, you can use instead:
std::string str = "Locale-specific string";
std::locale loc("en_US.UTF8"); // desired locale goes here
const ctype<char>& ct = use_facet<ctype<char> >(loc);
std::transform(str.begin(), str.end(), str.begin(), std::bind1st(std::mem_fun(&ctype<char>::tolower), &ct));
To answer your question directly and minus any context, you can call str.c_str()
to get const char *
(LPCSTR) with std::string
. You cannot directly convert std::string
to char *
(LPTSTR); this is by design and undermines some of the very motives for use std::string
.
source to share
void GetString(char * s, size_t capacity)
{
if (nullptr != s && capacity > 5)
{
strcpy_s(s,capacity, "Hello");
}
}
void FooBar()
{
std::string ss;
ss.resize(6);
GetString(&ss[0], ss.size());
std::cout << "The message is:" << ss.c_str() << std::endl;
}
As you can see, you can use the "old school pointer" both to feed strings to the legacy function and to be used as an OUT parameter. Of course, you need to make sure there is enough capacity in the row to work, etc.
source to share
Depending on your requirements, you can use one or more of the following:
-
std::string::operator[]()
... This function returns the character at the given index without bounds checking. -
std::string::at()
... This function returns the character at the given index with bounds checking. -
std::string::data()
... These functions return a pointerconst
to raw data. -
std::string::c_str()
... This function returns the same value asstd::string::data()
source to share