1

I want to get a log message from a C library. The library provides two functions :

int get_log_length(); // Returns the length of the string, including the terminating null character
void get_log(char *buffer); // Writes the log into buffer

I want to write that log into a std::string and return it. The normal way (I think) would be :

std::string get_log() {
    int length = get_log_length();
    char *buffer = new char[length];
    get_log(buffer);
    std::string str(buffer, length - 1);
    delete[] buffer;
    return str;
}

But I would like to skip using a buffer, so I thought of doing this :

std::string get_log() {
    std::string str;
    str.reserve(get_log_length());
    get_log(str.data()); // Bad: undefined behaviour
    return str;
}

But the size() of the std::string would remain unchanged, and would result in undefined behaviour (cf. cppreference).

Is there a way to only write the log once ?

1 Answer 1

4

You'll want to resize the string instead of reserve:

std::string get_log() {
   std::string str;
   str.resize(get_log_length());
   get_log(str.data());
   return str;
}

This will update the string's underlying buffer to the appropriate size AND fill the string out with '\0' characters. Afterwards, those characters are overwritten by get_log

One thing to be wary of, though, is that get_log is likely going to try to write a \0 terminator to the string, which is not what you want in a std::string, so you may need to lop the last character off.

OR, the other way around; get_log_length() might return the size of the log message minus the terminating character (like strlen would), but get_log might try to write a terminating '\0' anyway, meaning that you'd need to size you string as get_log_length() + 1, and then afterwards str.pop_back to remove the terminator. Just double check this behavior.

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

7 Comments

The easiest way to avoid the extra \0 is to do .resize(get_log_length()-1), rather than resizing the string later. Then get_log is going to overwrite the existing null-terminator instead of adding a new one.
@HolyBlackCat yeah that's a possibility. There's always chance for these kinds of off-by-one errors when going from C strings to C++ std::strings
@EuroMicelli: data() has a non-const overload in C++17
@EuroMicelli: And it was admittedly a little late in coming; vector had a non-const data function since C++11. Writing to the buffer returned by the non-const overload is not UB
@EuroMicelli yes, there is: &str[0] instead of str.data(). From C++11 onward, this is guaranteed to work ok. In practice, it will work in most implementations of earlier versions, too
|

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.