Search code examples
c++c++11vectordynamic-arrays

Pointer to std::vector's elements through std::vector.data() points to corrupt data


ON-TOPIC EDIT: This question is instead about an std::vector (defined inside a function) which got out of scope, but a pointer to its first element was returned through std::vector.data(). Trying to use the data pointed by the returned pointer resulted eventually in corrupt data, since the data inside the std::vector was already gone out of scope and freed. E.g.:

...
type *ptr;
type* function1(){
     std::vector<type> vector;
     return vector.data();
}
ptr = function1();
// ptr points to corrupt data here
...

ON-TOPIC SOLUTION: The "WAY A" written below is a solution, but it duplicates the data for a moment. Another thing to do would be, e.g.:

...
std::vector<type> vect;
void function1(std::vector<type> &vector){
     // Add data to vector
}
// Use the data inside vector
type *ptr = vect.data();
...

OLD QUESTION...

THE CONTEXT OF THE CONTEXT: I'm writing this engine where I store position, normal, vertex, and index data in a Mesh class. I use dynamic arrays of the required OpenGL types, just like this:

class Mesh(){   
    ... 
    GLfloat *vertices;   
    GLubyte *indices;   
    ...
}

I designed a cube with pen and paper, with its vertices, texture coordinates, normals and indices, and then filled my dynamic arrays with that data (copied it by hand, std::copy it to the dynamic arrays after proper memory allocation (using new btw). This worked like a charm. Cube is rendered perfectly with something like this:

...
glEnableClientState(GL_VERTEX_ARRAY);    
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
... 
glVertexPointer(3, GL_FLOAT, 0, meshInst->pos_coords);    
glTexCoordPointer(2, GL_FLOAT, 0, meshInst->tex_coords);
...
glDrawElements(
        GL_TRIANGLES, (CORRECT index count), GL_UNSIGNED_BYTE,
        meshInst->indices
    );
...

THE CONTEXT: I created another constructor for Mesh, which instead of copying the data to newly allocated memory from an array of GLfloat, GLubyte, etc. (wich worked like a charm i might repeat), it does the following:
1)Loads data (wich was thoroughly tested to be imported correctly) from a file to std::vector<GLfloat> positions, std::vector<GLubyte> inds, etc.
2)I tried two ways of passing the data to my dynamic arrays to be rendered by OpenGL, way A and way B:


WAY A:

std::copy(positions.begin(), positions.end(), vertices);
std::copy(inds.begin(), inds.end(), indices);

RESULT: WORKS PERFECTLY LIKE IN THE FIRST CONSTRUCTOR
DATA PRINT:

P[72] = { 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1 }
I[36] = { 0, 1, 2, 3, 2, 1, 6, 4, 7, 5, 7, 4, 10, 8, 9, 11, 9, 8, 13, 12, 15, 14, 15, 12, 17, 16, 19, 18, 19, 16, 23, 20, 21, 22, 21, 20 }

WAY B:

vertices = positions.data();
indices = inds.data();

RESULT: NOTHING IS DRAWN
DATA PRINT:

P[72] = { 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1 }
I[36] = { 0, 1, 2, 3, 2, 1, 6, 4, 7, 5, 7, 4, 10, 8, 9, 11, 9, 8, 13, 12, 15, 14, 15, 12, 17, 16, 19, 18, 19, 16, 23, 20, 21, 22, 21, 20 }

THE QUESTION IS... WHY? It seems to me that the issue is inside OpenGL, mainly because 'std::vector's are "guaranteed to be stored in contiguous storage locations in the same order as represented by the vector". As stated in http://www.cplusplus.com/reference/vector/vector/data/. Also if I compare the data pointed by 'vertices' and 'indices', it is literally identical. Compare both data prints for yourself. They are both printed using stored lengths and the dynamic array pointers right after loading.

THANK YOU VERY FOR YOUR TIME AND PLEASE REPLY WITH NO REMORSE, I LOVE TO LEARN. PS: Forgive my English, it's my second language.


Solution

  • Probably your std::vector is going out of scope. Since glVertexPointer does not create a deep copy, but just stores the pointer you must make sure, your original vector is still there, when you want to draw from is.

    Recommended solution: Transfer the data into a vertex buffer object, then you no longer have to deal with problems like this and as a benefit you get better performance as well.