positions of attributes

A

Axel F

Hi,

I have question about the following.
For instance I have a class like

class Cvec3
{
public:
float* begin() { return &x; }
operator float*() { return &x; }
public:
float x,y,z;
};

and I want to use c-function which take a pointer to float like some gl
functions. So I have defined the function begin(). I know this works and I
had never problems with this.
Somebody of my team says now, that it is not secure to do this, cause the
compiler can reorder the members x,y,z or can change pack alignment. He says
that nothing can be found about this in the c++ standard.

I hope somebody can help me to get the right arguments.

Thanks in advance,
Axel
 
B

Ben Pope

Axel said:
Hi,

I have question about the following.
For instance I have a class like

class Cvec3
{
public:
float* begin() { return &x; }
operator float*() { return &x; }
public:
float x,y,z;
};

and I want to use c-function which take a pointer to float like some gl
functions. So I have defined the function begin(). I know this works and I
had never problems with this.
OK.

Somebody of my team says now, that it is not secure to do this, cause the
compiler can reorder the members x,y,z or can change pack alignment. He says
that nothing can be found about this in the c++ standard.

I'm not sure how that would be relevant.
I hope somebody can help me to get the right arguments.

How do you intend to use these functions? (I've modified your code
slightly, but not in any way that changes the problem)

#include <iostream>

class Cvec3 {
public:
Cvec3() : x(1.1), y(1.2), z(1.3) {}; // Initialise members
float* begin() { return &x; }
operator float*() { return &x; }
private:
float x,y,z;
};

// ok, not a real C function, but close enough
void myCFunc(float* f) {
std::cout << *f << std::endl;
}

int main() {
Cvec3 v;
myCFunc(v.begin());
myCFunc(v);
}

There's absolutely nothing wrong with that, providing v exists for at
least as long as myCFunc requires it to.

Ben Pope
 
J

Jerry Coffin

Axel said:
Hi,

I have question about the following.
For instance I have a class like

class Cvec3
{
public:
float* begin() { return &x; }
operator float*() { return &x; }
public:
float x,y,z;
};

and I want to use c-function which take a pointer to float like some gl
functions. So I have defined the function begin(). I know this works and I
had never problems with this.
Somebody of my team says now, that it is not secure to do this, cause the
compiler can reorder the members x,y,z or can change pack alignment.

If you're using this as I expect you are, he's basically right. My
guess is that you're using begin as a pointer not just to x, but also
to y and z For example, indexing off of the pointer it returns and
treating x, y and z as a vector. You can fix part of the problem fairly
easily, but not all of it. If you use:

struct Cvec3 {
float *begin() { return &x; }
operator float*() { return &x; }

float x, y, z;
};

This fixes some of the problems -- the compiler can no longer reorder
the data, but it can still insert padding between x, y and Z. If you
want to fix this, things get a bit more complex:

struct Cvec3 {
float *begin() { return &x; }
operator float*() { return &x; }

float data[3];

float &x, &y, &z;

Cvec3() : x(data[0]), y(data[1]), z(data[2]) {}
};

Now the three floats are guaranteed to be stored contiguously -- i.e.
the compiler can't insert any padding between them. The x, y and z are
just references to the three locations in data, so it makes basically
no diference whether you use data[1] or y -- the two are basically just
two ways of referring to the same thing.

--
Later,
Jerry.





He says
 
A

Axel F

If you're using this as I expect you are, he's basically right. My
guess is that you're using begin as a pointer not just to x, but also
to y and z For example, indexing off of the pointer it returns and
treating x, y and z as a vector. You can fix part of the problem fairly
easily, but not all of it. If you use:

struct Cvec3 {
float *begin() { return &x; }
operator float*() { return &x; }

float x, y, z;
};

This fixes some of the problems -- the compiler can no longer reorder
the data, but it can still insert padding between x, y and Z. If you
want to fix this, things get a bit more complex:

Thank you for your answer.
the padding seems to be no problem, cause x,y,z are of the same type
(4-byte), so that all elements are placed serial in memory also if I use
8-byte alignment. And if I allocate an array with 'new Cvec3[n]' all
elements are stored serial.
But back to the reorder problem. You say that reorder is not a problem if I
use a struct instead of a class with public/private members, right?

Regards, Axel
 
M

Matthias Kluwe

Hi!

Axel F said:
class Cvec3
{
public:
float* begin() { return &x; }
operator float*() { return &x; }
public:
float x,y,z;
};

and I want to use c-function which take a pointer to float like some gl
functions. So I have defined the function begin(). I know this works and I
had never problems with this.
Somebody of my team says now, that it is not secure to do this, cause the
compiler can reorder the members x,y,z or can change pack alignment. He says
that nothing can be found about this in the c++ standard.

Hmm, I can't call me an expert, but do you want do rely on (c being an Cvec3 object) c.begin()+1 pointing to y? That looks risky to me.

Regards,
Matthias
 
R

roberts.noah

Axel said:
Hi,

I have question about the following.
For instance I have a class like

class Cvec3
{
public:
float* begin() { return &x; }
operator float*() { return &x; }
public:
float x,y,z;
};

and I want to use c-function which take a pointer to float like some gl
functions. So I have defined the function begin(). I know this works and I
had never problems with this.
Somebody of my team says now, that it is not secure to do this, cause the
compiler can reorder the members x,y,z or can change pack alignment. He says
that nothing can be found about this in the c++ standard.

He/she is right in that you cannot depend on y immediately following x
or anything about their relative locations. begin()++ could be or do
anything.

If something is not specifically specified by the standard it is
undefined (see definition of undefined behavior 1.3.12)...meaning there
is absolutely no restriction on behavior. I don't know if the fact
that you can't depend on packing and ordering is specified or not.
 
R

roberts.noah

Axel said:
Thank you for your answer.
the padding seems to be no problem, cause x,y,z are of the same type
(4-byte), so that all elements are placed serial in memory also if I use
8-byte alignment.

It is definately a problem. It is undefined behavior. It may work for
you now but could break in ways VERY difficult to figure out at any
time. Once again, you cannot depend on the behavior of your construct;
the _only_ thing you can depend on is the return value of begin() will
be the address of the member 'x'.

It's like having a car that could explode for no reason whatsoever at
any moment...would you drive it even though it is currently working and
seems to be safe??!! Using your class could cost many hours of work
down the road and it would be very ill-advised to use in any project,
much less anything of production value.
 
F

Frank Neuhaus

It is definately a problem. It is undefined behavior. It may work for
you now but could break in ways VERY difficult to figure out at any
time. Once again, you cannot depend on the behavior of your construct;
the _only_ thing you can depend on is the return value of begin() will
be the address of the member 'x'.

What if you make it a union like this?

union
{
float data[3];
struct
{
float x;
float y;
float z;
}
};

Doesnt that enforce the alignment of x,y,z because data is aligned to 4 byte
boundaries?

If it doesnt, you could still use a #pragma pack(4) to enforce it
 
R

roberts.noah

Frank said:
It is definately a problem. It is undefined behavior. It may work for
you now but could break in ways VERY difficult to figure out at any
time. Once again, you cannot depend on the behavior of your construct;
the _only_ thing you can depend on is the return value of begin() will
be the address of the member 'x'.

What if you make it a union like this?

union
{
float data[3];
struct
{
float x;
float y;
float z;
} vars;
};

edit: gave struct a name...
Doesnt that enforce the alignment of x,y,z because data is aligned to 4 byte
boundaries?

AFAIK there is nothing saying vars is aligned to 4 byte boundaries.

data and vars need not be the same size. They do have the same
address, but beyond that there isn't much that is guaranteed. I don't
know if vars.x and data[0] have the same address for sure; I have often
seen code that assums so but someone more familiar with the std will
need to say if it is guaranteed behavior. I do know, from previous
discussions, that data[1] and vars.y don't have to align.
 
P

persenaama

That works but not very likely reflect the intended use for the class
(talking about the reference members). Ofcourse, if it isn't a problem
that sizeof(Cvec3) >= sizeof(float)*3 (more likely to be double or
triple the size on common target platforms.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top