Reputation: 20396
I'd like to write a template function which can pass data to a C-style API (in my use-case specifically, OpenGL), using a std::vector
. The code I came up with looks like this:
template<typename T>
void pass_data(GLuint buffer, std::vector<T> const& data) {
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(T), data.data(), GL_STATIC_DRAW);
}
This seemed like a simple solution, until it occurred to me that T
might have alignment restrictions that would cause &t[1] - &t[0] != sizeof(T)
to be true, which would mean that this function would not accurately pass the whole array. So I rewrote the function to do this instead:
template<typename T>
void pass_data(GLuint buffer, std::vector<T> const& data) {
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, static_cast<size_t>(&data[data.size()] - &data[0]), data.data(), GL_STATIC_DRAW);
}
However, it's not clear to me that this kind of one-past-the-end access is safe (I'm pretty sure it's undefined behavior). What's the best way to ensure that my function accurately assesses the byte-size of my vector, and passes the correct parameters to the underlying C-API call?
Note: I do not want discussion about how I'm [mis-]using OpenGL. I'm just focused on the desire to accurately model a pointer + size of a vector.
Upvotes: 2
Views: 64
Reputation: 72356
Yes, data[data.size()]
is undefined behavior.
Luckily, you don't need any such thing, because (char*)&data[1] - (char*)&data[0] == sizeof(T)
is in fact guaranteed for any type T
. So just go back to data.size() * sizeof(T)
.
But I would also add a
static_assert(std::is_trivially_copyable<T>::value, "Invalid type");
to be a bit safer.
Upvotes: 4