Access members of class with pointer

Viewed 183

I was looking an example of Modern OpenGL in c++ where a structure is used to hold data regarding vertex coordinates and a 3 coordinate color vector

struct VertexData{
    float x, y;
    float r, g, b;
}

std::vector<VertexData> myData = {
    {.x = 0, .y = 0, .r = 0, .g = 0, .b = 0},
    {.x = 0, .y = 0, .r = 0, .g = 0, .b = 0}
}

float *ptr = (float*) &myData[0];

To access the x value of the second element of the vector, I could acces ptr[5], technique used to pass a vertex buffer to OpenGL. My question is: Can I use this same technique with a more complex class? As a concrete example, if I have a Vertex class:

class Vertex {
public:
    float x, y, z;
    Vertex();
    // Other constructors here...
    Vertex operator+(const Vertex &other);
    // Overload of other operators like -,*,=, etc...
}
std::vector<Vertex> myData(100);

float *ptr = (float*) &myData[0];

would it still be safe (and a good idea...) to use the ptr variable to access all elements in this vector? I know this could be tricky in the presence of static variables, different types, inheritance and so on, but in this case were there are only plain variables and functions as members, would using a pointer be a reliable approach?

2 Answers

According to the C++ Standard, this technique has undefined behavior. The compiler is free to add padding between members. So, according to the principles of the standard, this is a bad idea.

However.

All compilers I know (I've worked with 8 different C++ compilers in the past 15 years), don't add padding in this case. Why would they? So, if you dare to enter undefined-behavior land, you can use this technique. Even in complex cases. Inheritance, virtual functions, etc. All the compilers I know, do the logical thing, and put float members next to each other in memory in your case, and their optimizers don't fail for this case. Note: the standard allows compilers reorder members between access specifiers, so if you want to be on the safe side (which you've already left), put all members under the same access specifier (still, I've yet to see a compiler, which actually reorders members between access specifiers.).

So. Is it reliable? According to the standard: No. According to the current practice: in my experience, yes. I've yet to see a compiler, for which such a code fail. I encourage anyone who knows such a compiler, to not be silent, and add it as an answer.

Note: this is the current status. It may break any time. I'd only recommend using this technique if you know what you're doing, and you're aware of the possible consequences. And don't forget to comment this fact in your code.

As others already mentioned, this method of yours is not reliable, because the compiler may choose to pad your struct. For example, if you are on a 64 bit platform, the compiler may choose to pad the struct to 8-byte align.

But the idea of using a pointer to access members of class still can work. You can use sizeof to get the actual size of the struct (in bytes).

With your first example, it would look like this:

float *ptr = (float*) &myData[0].x;

and access the next x value like this:

ptr = (float*)(((char*)ptr) + sizeof(VertexData));

or, alternatively, if you are sure that sizeof(VertextData) is a multiple of sizeof(float), it could be:

ptr += sizeof(VertexData) / sizeof(float);

This is a bit complicated, but is reliable. Also, you can wrap things into macros, if you wish.

Whether this method is practical depends on your particular situation. I'd say in certain situations it could be useful.


EDIT:

There is, nevertheless, some danger in using this method.

The most important thing to remember is that, if you insert some elements into your vector, the whole storage could be reallocated, hence your previous pointer will no longer be valid.


EDIT:

There is also another way, avoiding the use of sizeof. To access the next x value, you can use:

ptr = (float*) (((VertexData*) ptr) + 1);

This should compile to the same result.

Related