How to get a writable C buffer from std::string?

4.6k views Asked by At

I'm trying to port my code from using MFC's CString to std::string for Microsoft Windows platform. And I'm curious about something. Say in the following example:

CString MakeLowerString(LPCTSTR pStr)
{
    CString strLower = pStr ? pStr : L"";
    CharLower(strLower.GetBuffer());        //Use WinAPI
    strLower.ReleaseBuffer();

    return strLower;
}

I use strLower.GetBuffer() to obtain a writable buffer to be passed to the CharLower API. 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?

4

There are 4 answers

10
Special Sauce On

To lowercase a std::string containing only ASCII characters, you can use this code:

#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 original Windows API call would be doing the same character iteration internally.

If you need to get toLower() for multi-byte encodings (e.g. UTF-8), or a locale 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 a const char * (LPCSTR) from a std::string. You cannot directly convert a std::string to a char * (LPTSTR); this is by design and would undermine some of the very motivations for using std::string.

5
R Sahu On

Depending on your requirements, you can use one or more of the following:

  1. std::string::operator[](). This function returns a character at a given index without bounds checking.

  2. std::string::at(). This function returns a character at a given index with bounds checking.

  3. std::string::data(). This functions returns an const pointer to the raw data.

  4. std::string::c_str(). This function returns the same value as std::string::data()

10
BitTickler On
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 the "old school c- pointer" both for feeding strings into a legacy function as well as use it as an OUT parameter. Of course, you need to make sure, there is enough capacity in the string for it to work etc.

0
Peter Rawytsch On

On my new job we do not use MFC - but luckily std lib and C++11 - so I've come up to the same question as c00000fd. Thanks to BitTickler's answer I came up with the idea of using the string's internal buffer for Win32-APIs via the &s[0] resp. &s.front() catch.

Using a Win32-API function that shrinks the internal string buffer

Assuming that you have a string which shall become shortened by a Win32-API function - e.g. ::PathRemoveFileSpec(path) - you may follow this approach:

std::string path( R("?(C:\TESTING\toBeCutOff)?") );
::PathRemoveFileSpec( &path.front() ); // Using the Win32-API
                                       // and the the string's internal buffer
path.resize( strlen( path.data() ) );  // adjust the string's 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's internal buffer
path.resize( wcslen( path.data() ) );  // adjust the string's 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 extends the internal string buffer

Assuming that you have a string which shall become extended or filled by a Win32-API function - e.g. ::GetModuleFileName(NULL, path, cPath) to retrieve your executable's path - you may follow this approach:

std::string path;
path.resize(MAX_PATH);                 // adjust the internal buffer's 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's internal buffer
path.resize( strlen( path.data() ) );  // adjust the string's 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's 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's internal buffer
path.resize( wcslen( path.data() ) );  // adjust the string's 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 then you need just one more line of code when extending the string's internal buffer compared with the MFC alternative, when shrinking the string it has nearly the same overhead.

The advantage of the std::string approach in contrast to the CString approach is that you do not have to declare an additional C-String pointer variable, you just work with the official std::string methods and with one strlen/wcslen function. My approach shown above only works for the shrinking variant when the resulting Win32-API buffer is null-terminated, but for that very special case in which the Win32-API returns an unterminated string, then - similar to the CString::ReleaseBuffer method - you must explicitly know and specify the new string/buffer length by path.resize( newLength ) - just like path.ReleaseBuffer( newLength ) for the CString alternative.