10

It is well defined C++ to pass std::vector to C APIs that expect an output array like so because std::vector is contiguous:

std::vector<char> myArray(arraySize);
cStyleAPI(&myArray[0], arraySize);

Is it safe to pass std::string in the same manner to C APIs? Is there any guarantee in standard C++03 that std::string is contiguous and works in the same way as std::vector does in this situation?

6 Answers 6

11

If the C API function requires read-only access to the contents of the std::string then use the std::string::c_str() member function to pass the string. This is guaranteed to be a null terminated string.

If you intend to use the std::string as an out parameter, C++03 doesn't guarantee that the stored string is contiguous in memory, but C++11 does. With the latter it is OK to modify the string via operator[] as long as you don't modify the terminating NUL character.

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

13 Comments

c_str() returns a const char *, and I need to be able to receive output into the string. Is it ok to do this without using const_cast and c_str()?
@masrtis No, it still isn't, but then why don't you use char buf[BUFSIZE]?
@masrtis Well, practically on most reasonable implemtations? Yes! But theoretically guaranteed by standard? Definitely not!
@Praetorian: So long as const_cast rules are not violated and compiler knows it is being casted away (side effects).
@Joel I never said you should do that. The first part was before I realized the OP intended to use the string as an out parameter. The second sentence says you should use operator[] to modify the string. I've updated the answer to be clear.
|
6

So, I know this has been answered already, but I saw your comment in Praetorian's answer:

It's an OpenGL driver bug that results in the return value for the maximum length string being broken. See https://forums.geforce.com/default/topic/531732/glgetactiveattrib-invalid/. glGetActiveAttrib won't try to write to the pointer returned by the new[] call with the 0 size allocation, but the string isn't null terminated. Then, later in the code, the non-null terminated string is copied into a std::string for storage, which results in a read buffer overflow. Very confusing to me too, and just checking here to see if std::string would make things easier to follow.

Uhm... forgive me but if this is your problem then all these solutions seem to be overly complicated. If the problem boils down to the fact that you get a 0 as the buffer size that you need (which means that you will end up with a string that's not NULL-terminated, since there's no space for the NULL terminator) then simply make sure a NULL terminator is always present:

int arraySize; 

/* assume arraySize is set to the length we need */
...

/* overallocate by 1 to add an explicit NULL terminator. Just in case. */
char *ptr = malloc(arraySize + 1);

if(ptr != NULL)
{
    /* zero out the chunk of memory we got */
    memset(ptr, 0, arraySize + 1);

    /* call OpenGL function */
    cStyleAPI(ptr, arraySize);

    /* behold, even if arraySize is 0 because of OpenGL, ptr is still NULL-terminated */
    assert(ptr[arraySize] == 0);

    /* use it */
    ...

    /* lose it */
    free(ptr);
}

This seems, to me, to be the simplest, sanest solution.

4 Comments

Should be the simplest, yes. Just curious: Any particular reason for using malloc & memset and not calloc?
Or new[] and delete[], for that matter?
As I said - just habit. Feel free to use your favorite memory allocator ;)
+1, the fix that I've put in place for now does something very similar to this, though I still have some doubts as to how errors are handled in the surrounding code that may make using this solution messier. This question has also come up in other situations in the past so I wanted to see what my options were if I find myself asking it again in the future.
1

Yes, but you'll need to pass them using the c_str method to guarantee null-termination.

Comments

1

use resize_and_overwrite

https://en.cppreference.com/w/cpp/string/basic_string/resize_and_overwrite

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1072r10.html

[[nodiscard]] static inline string formaterr (DWORD errcode) {
    string strerr;
    
    strerr.resize_and_overwrite(2048, [errcode](char* buf, size_t buflen) {
        // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage
        return FormatMessageA(
            FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr,
            errcode,
            0,
            buf,
            static_cast<DWORD>(buflen),
            nullptr
        );
    });
    
    return strerr;
}

Comments

0

No, it isn't, but generally because C strings are assumed to be zero-terminated, which your pointer-to-char isn't. (If you use string, you can use string::c_str() instead, that's 0-terminated.)

Anyways, C++11 does require the elements of vector to be contiguous in memory.

Comments

0
cStyleAPI(&myArray[0], arraySize);

If your cStyleAPI receives a char* for input, that is what std::string::c_str() is for.

If it receives a pre-allocated char* for output, then no. In that case, you should use a std::vector<char> or std::array<char>.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.