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?

masrtis
  • 1,282
  • 17
  • 32

6 Answers6

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 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 May 07 '13 at 17:48
  • @masrtis No, it still isn't, but then why don't you use `char buf[BUFSIZE]`? –  May 07 '13 at 17:49
  • @masrtis Well, practically on most reasonable implemtations? Yes! But theoretically guaranteed by standard? Definitely not! – Christian Rau May 07 '13 at 17:50
  • @Praetorian: So long as [`const_cast`](http://en.cppreference.com/w/cpp/language/const_cast) rules are not violated and compiler knows it is being casted away (side effects). –  May 07 '13 at 17:51
  • @Vlad You don't need to `const_cast` at all. Use `&mystr[0]` to get a non-const pointer to the beginning of the string. But, of course, `mystr` should not be `const` to begin with. – Praetorian May 07 '13 at 17:53
  • @H2CO3 I don't have any indication of what the buffer size should be at compile time. The current code I'm working with dynamically allocates a string, but due to a bug in an external library, will sometimes return 0 for the size of the string. Using new[] in this case leads to a read buffer overflow later on in the code because it's UB to dereference the pointer returned by new[] in that case. Using std::string would hopefully remove the need to account for the null terminator manually. – masrtis May 07 '13 at 17:54
  • @masrtis I'm confused. This external library reports you need a zero length string, but will then try to write characters to the pointer you pass to it? There's no way to protect against such a thing, is there? The best you can do is allocate enough room for a maximum string length (if such a maximum exists) and pass that to the library function in all cases. If you initialize the allocated memory to `0`, NULL termination is guaranteed in all circumstances. Whether you're using vector or string doesn't matter in such a case because you need to size both appropriately prior to calling C API – Praetorian May 07 '13 at 17:59
  • @Praetorian 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. – masrtis May 07 '13 at 18:09
  • @masrtis `std::string` has a [constructor](http://en.cppreference.com/w/cpp/string/basic_string/basic_string) that takes a `char *` and number of characters to be read from the source string (#4 on that page). You could use that instead of relying on NULL termination. Doesn't that fix the issue? – Praetorian May 07 '13 at 18:14
  • You can still not modify the result of c_str() and have it guaranteed to be reflected in the std::string. – Joel May 07 '13 at 18:15
  • 1
    @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. – Praetorian May 07 '13 at 18:21
  • @Praetorian Using that constructor might be ok in this specific case. – masrtis May 07 '13 at 18:31
  • @masrtis It seems to me that you're overcomplicating things. With your comment above as a hint, I posted an solution that, to me anyways, seems lot more sensible. – Nik Bougalis May 07 '13 at 18:49
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.

Nik Bougalis
  • 10,495
  • 1
  • 21
  • 37
  • Should be the simplest, yes. Just curious: Any particular reason for using `malloc` & `memset` and not `calloc`? – Daniel Fischer May 07 '13 at 19:02
  • Or new[] and delete[], for that matter? – Joel May 07 '13 at 19:14
  • As I said - just habit. Feel free to use your favorite memory allocator ;) – Nik Bougalis May 07 '13 at 19:17
  • +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. – masrtis May 07 '13 at 20:16
1

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

hd1
  • 33,938
  • 5
  • 80
  • 91
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.

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>.

utnapistim
  • 26,809
  • 3
  • 46
  • 82
0

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;
}
Hongfei Shen
  • 103
  • 1
  • 7