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