Misuse of GL info log null terminating character i

2019-05-16 17:41发布

问题:

I have a fairly simple log() method for a GL shader and program convenience classes, since the respective compile and link methods only return a bool, while hiding all the GL calls; e.g.,

std::string
glShader::log () const
{
    std::string info;
    GLint len = 0;

    if (!glIsShader(gl_shader_obj))
        info = "(invalid shader object)\n";
    else
        glGetShaderiv(gl_shader_obj, GL_INFO_LOG_LENGTH, & len);

    if (len != 0)
    {
        info.resize(static_cast<std::string::size_type>(len));
        glGetShaderInfoLog(gl_shader_obj, len, NULL, & info[0]);
    }

    return info;
}

Is this a misuse of the std::string::resize (size_type) argument? I known that C++11 mandates a null terminating character when queried, i.e., c_str(); but does it guarantee its presence in the storage? This might be a 'reasonable' way to implement string to simplify C string access, but not a requirement.

However, GL_INFO_LOG_LENGTH includes \0 in the number of characters in the log, provided there is a log; otherwise the log length is simply zero.

Am I potentially writing past the end of the string's reserved buffer in this fashion? Should I be using (len - 1) in the InfoLog call? Or do I have the idea about C++11 strings wrong? That is, can I safely overwrite with the null terminator?

回答1:

Yes, this is a valid use of std::string. You can get a pointer to the first character and write to the array of characters, so long as you don't exceed the range [0, size()).

However, you did make one mistake. See, GL_INFO_LOG_LENGTH includes the NUL terminator character in the length. Which means that, technically speaking, your std::string is one character longer than it needs to be. The last character of info will be a NUL character, and the std::string will treat that like it is part of the string's data, rather than a delimiter marking the end of the string.

You should not try to fix this by subtracting 1 from len before setting info's size. Why? Because glGetShaderInfoLog will always NUL terminate the string it writes. So if you shrink len, it will chop off the last actual character from the log.

Instead, you should shrink info after you've copied it from OpenGL:

info.resize(static_cast<std::string::size_type>(len));
glGetShaderInfoLog(gl_shader_obj, len, NULL, & info[0]);
info.pop_back();

In C++17, the standard wording has changed to allow writing to the NUL-terminator of a string, so long as you are overwriting it with a NUL character. It also allows string::data to return a non-const character array. Therefore, this will work fine now:

info.resize(static_cast<std::string::size_type>(len - 1));
glGetShaderInfoLog(gl_shader_obj, len, NULL, info.data());

The standard requires that info will have size+1 bytes in it.