Xirema
Xirema

Reputation: 20396

Undefined Behavior to take Address of End of Array/Vector?

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

Answers (1)

aschepler
aschepler

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

Related Questions