473,406 Members | 2,843 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

is this portable, conforming to standard, elegant?

class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};

access with data array is convenient for file io and x,y,z for other things
Feb 9 '07 #1
32 1986
"r.z." <gk***@hjkjhk.plwrote in news:eq**********@nemesis.news.tpi.pl:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};

access with data array is convenient for file io and x,y,z for other
things

Is it possible that the structure may be byte-packed in a different manner
than an array?
Feb 9 '07 #2
r.z. wrote:
class vector3
{
public:
union
{
float[3] data;
syntax error.
float data[3];
struct
{
float x, y, z;
};
};
};

access with data array is convenient for file io and x,y,z for other things
Depends on how you use it. Behavior is only defined if you access the
same member of the union that you last modified. In other words, if you
write to x, and try to read it with data[0], you get UB.
Feb 9 '07 #3
Is it possible that the structure may be byte-packed in a different manner
than an array?
do you ask whether data[] will always contain values x, y, z in this order?
(like in the structure) then the answer is yes.
Feb 9 '07 #4
>
Depends on how you use it. Behavior is only defined if you access the
same member of the union that you last modified. In other words, if you
write to x, and try to read it with data[0], you get UB.
bad news :/
Feb 9 '07 #5
Depends on how you use it. Behavior is only defined if you access the
same member of the union that you last modified. In other words, if you
write to x, and try to read it with data[0], you get UB.
but data[0] and x are still the same types? aren't they?
Feb 9 '07 #6
r.z. wrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};

access with data array is convenient for file io and x,y,z for other
things
If you mix access modes, you get undefined behavior. However, you can fake
the interface as follows:

struct Vector3 {

float data [3];
float & x;
float & y;
float & z;

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

};

#include <iostream>

int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y << '\n';
}
An obvious drawback is that assignment and copy-construction become tricky.
Also, it is not clear whether the compiler will realize the reference
members as pointers and allocate additional space. Therefore, the following
seems to be better:

struct Vector3 {

float data [3];

float & x ( void ) {
return ( data[0] );
}

float const & x ( void ) const {
return ( data[0] );
}

float & y ( void ) {
return ( data[1] );
}

float const & y ( void ) const {
return ( data[1] );
}

float & z ( void ) {
return ( data[2] );
}

float const & z ( void ) const {
return ( data[2] );
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y() << '\n';
}
Now, there is still the issue that the user could easily do data[4] and
trigger undefined behavior. Thus, maybe:

#include <cassert>

struct Vector3 {

static unsigned int const size = 3;

private:

float data [size];

public:

Vector3 ( void )
: data ()
{}

float & x ( void ) {
return ( data[0] );
}

float const & x ( void ) const {
return ( data[0] );
}

float & y ( void ) {
return ( data[1] );
}

float const & y ( void ) const {
return ( data[1] );
}

float & z ( void ) {
return ( data[2] );
}

float const & z ( void ) const {
return ( data[2] );
}

float & operator[] ( unsigned i ) {
assert( i < size );
return ( data[i] );
}

float const & operator[] ( unsigned i ) const {
assert( i < size );
return ( data[i] );
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y() << '\n';
}

Finally, you may consider to use double instead of float.
Best

Kai-Uwe Bux
Feb 9 '07 #7
r.z. wrote:
>Depends on how you use it. Behavior is only defined if you access the
same member of the union that you last modified. In other words, if you
write to x, and try to read it with data[0], you get UB.

but data[0] and x are still the same types? aren't they?
Sure, both are float (given the context from the OP that has been lost due
to editing). But why would that matter? You have undefined behavior anyway.
Best

Kai-Uwe Bux
Feb 9 '07 #8
red floyd a écrit :
r.z. wrote:
>class vector3
{
public:
union
{
float[3] data;
syntax error.
float data[3];
> struct
{
float x, y, z;
};
};
};

access with data array is convenient for file io and x,y,z for other things

Depends on how you use it. Behavior is only defined if you access the
same member of the union that you last modified. In other words, if you
write to x, and try to read it with data[0], you get UB.

From the standard draft(2135) §9.5 (but it is the same in the standard):
[Note: one special guarantee is made in order to simplify the use of
9.2), and if an object of unions: If a POD-union contains several
POD-structs that share a common initial sequence ( this POD-union type
contains one of the POD-structs, it is permitted to inspect the common
initial sequence of any of POD-struct members; see 9.2. —end note]

Then if you have:
enum
{
struct
{
int a;
int b;
float c
} one;

struct
{
int aa;
int bb;
char foo[42];
} two;
} data;

data.one.a=1;
Then accessing data.two.aa is permitted and equal to data.one.a.

In the case of the OP, the initialisation sequence is not the same so it
is UB although it looks naggingly close.

Michael

Feb 9 '07 #9
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
r.z. wrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};
access with data array is convenient for file io and x,y,z for other
things

If you mix access modes, you get undefined behavior. However, you can fake
the interface as follows:

struct Vector3 {

float data [3];
float & x;
float & y;
float & z;

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

};

#include <iostream>

int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y << '\n';

}

An obvious drawback is that assignment and copy-construction become tricky.
Also, it is not clear whether the compiler will realize the reference
members as pointers and allocate additional space. Therefore, the following
seems to be better:

struct Vector3 {

float data [3];

float & x ( void ) {
return ( data[0] );
}

float const & x ( void ) const {
return ( data[0] );
}

float & y ( void ) {
return ( data[1] );
}

float const & y ( void ) const {
return ( data[1] );
}

float & z ( void ) {
return ( data[2] );
}

float const & z ( void ) const {
return ( data[2] );
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y() << '\n';

}

Now, there is still the issue that the user could easily do data[4] and
trigger undefined behavior. Thus, maybe:

#include <cassert>

struct Vector3 {

static unsigned int const size = 3;

private:

float data [size];

public:

Vector3 ( void )
: data ()
{}

float & x ( void ) {
return ( data[0] );
}

float const & x ( void ) const {
return ( data[0] );
}

float & y ( void ) {
return ( data[1] );
}

float const & y ( void ) const {
return ( data[1] );
}

float & z ( void ) {
return ( data[2] );
}

float const & z ( void ) const {
return ( data[2] );
}

float & operator[] ( unsigned i ) {
assert( i < size );
return ( data[i] );
}

float const & operator[] ( unsigned i ) const {
assert( i < size );
return ( data[i] );
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y() << '\n';

}

Finally, you may consider to use double instead of float.

Best

Kai-Uwe Bux- Skjul tekst i anførselstegn -

- Vis tekst i anførselstegn -
Your solution has the nice property that it is portable and does not
break the standard. The problem is that the references will take up
space on many compilers - or at least did when I tried to use the same
trick.
The "union hack" is formally undefined behaviour, but in practice it
is very portable.

/Peter

Feb 9 '07 #10
On 9 Feb., 03:27, "r.z." <g...@hjkjhk.plwrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};

};

access with data array is convenient for file io and x,y,z for other things
It is neither elegant nor standards-conforming. Still, it is quite
portable and a hack that could be used in one specific situation,
namely the one where you have legacy code using xyz-notation but would
like to interface to code using arrays. Test it on the platform - e.g.
each time your program starts up - and live with it.
In all other situations, you should prefer a not-so-hackish solution.

/Peter

Feb 9 '07 #11
On 9 fév, 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
[sniped a LOT of code]
struct Vector3 {

float data [3];
float & x;
float & y;
float & z;

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

};
Kai-Uwe Bux
A common trick (which I first saw in a gamedev.net thread) is to use a
static array as a proxy:

class vector3d
{
static float vector3d::* proxy[3];
public:
float x, y, z;
float& operator[](unsigned int i) { return (*this).*proxy[i]; }
};

float vector3d::* vector3d::proxy[3] = { &vector3d::x, &vector3d::y,
&vector3d::z };

I'm not sure about the initialization time of proxy[], but beside that
this code looks ok (I haven't checked it). sizeof(vector3d) is the
expected size, and we provide fast access to the data when we use
operator[]. best of all world, IMHO.

Regards,

-- Emmanuel Deloget

Feb 9 '07 #12

"r.z." <gk***@hjkjhk.plwrote in message
news:eq**********@nemesis.news.tpi.pl...
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};

access with data array is convenient for file io and x,y,z for other
things

Aside from the issues the others have already raised, your struct
declaration doesn't declare or define anything - it has no typename and it
is not a member definition. There are compilers that support this kind of
syntax (with the same behaviour as with a union - they insert the members in
the scope in which the union is being defined), but it is not conforming to
standard C++.

- Sylvester
Feb 9 '07 #13

"peter koch" <pe***************@gmail.comwrote in message
news:11*********************@m58g2000cwm.googlegro ups.com...
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
>
*snap*
>
The "union hack" is formally undefined behaviour, but in practice it
is very portable.

/Peter
I'm still waiting for the compiler that actually *does* generate code that
formats your harddrive whenever it encounters UB ;)

- Sylvester
Feb 9 '07 #14
Sylvester Hesp a écrit :
"peter koch" <pe***************@gmail.comwrote in message
news:11*********************@m58g2000cwm.googlegro ups.com...
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
*snap*
>The "union hack" is formally undefined behaviour, but in practice it
is very portable.

I'm still waiting for the compiler that actually *does* generate code that
formats your harddrive whenever it encounters UB ;)
Easy.

If the undefined behaviour causes a bug very difficult to locate, the
tester may well put his hard drive in a microwave just to be rid of the
code and start anew. He may put the backups in it as well.
Michael

Feb 9 '07 #15
On Feb 9, 7:16 am, "Sylvester Hesp" <s.h...@oisyn.nlwrote:
"peter koch" <peter.koch.lar...@gmail.comwrote in message

news:11*********************@m58g2000cwm.googlegro ups.com...
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:

*snap*
The "union hack" is formally undefined behaviour, but in practice it
is very portable.
/Peter

I'm still waiting for the compiler that actually *does* generate code that
formats your harddrive whenever it encounters UB ;)

- Sylvester
It is interesting that union hacking is undefined behavior, whereas
reinterpret_cast has implementation-defined behavior.

In the (non-normative) example in 9.5 para 2 of my copy of the draft
standard (which has probably moved), it shows an anonymous union { int
a; char *p ; } and notes that a and p are ordinary variables which
have the same address. Local variables may be placed in registers -
sometimes even if their address is taken - in which case the compiler
could reasonably place a and p in different registers (especially if
sizeof(int) != sizeof(p)), as that would not affect any fully-
complient program. For example,

void test( bool set_int, bool get_int )
{
union { int a; char *p ; };
if( set_int ) a = 42;
else p = "hello";

if( get_int ) std::cout << a << std::endl;
else std::cout << p << std::endl;
}

test(false, false) ["42"] and test(true, true) ["hello"] are both well-
defined. test(true, false) is undefined and likely to crash.

test( false, true ) may be expected to print the address of the local
string, but - while it probably won't actually format your hard drive
- there are many "reasonable" types of undefined behavior that a
conscientious (i.e. non-malicious) compiler writer may cause to
happen. These include outputting the string address, outputting "42",
or outputting a random integer; architectures which reserve 0x800..000
as an integer NaN may even helpfully tell that a was uninitialized!

The code may have been effectively rewritten as:

void test( bool set_int, bool get_int )
{
register int a; // = NaN
register char *p ;
if( set_int ) a = 42;
else p = "hello";

if( get_int ) std::cout << a << std::endl;
else std::cout << p << std::endl;
}

which would give a garbage (or uninitialized) integer value.

This may have been optimized to:

void test( bool /*set_int*/, bool get_int )
{
register int a = 42; // only value actually used for a.
register char *p = "hello"; // only value actually used for p.

if( get_int ) std::cout << a << std::endl;
else std::cout << p << std::endl;
}

or equivalently:

void test( bool /*set_int*/, bool get_int )
{
if( get_int ) std::cout << 42 << std::endl;
else std::cout << "hello" << std::endl;
}

Using reintrepret_cast on the data should be more reliable - just read
the compiler documentation ;-).

In practice, I must admit to using the "union hack" in low level code
- accessing hardware registers or implementing communications
protocols - without problem. However, this is library code that is
known to be non-portable, and has a suite of unit tests which get run
on each new compiler version.

I can't think of any reasons, outside maintenence situations - where
the union hack would apply to application code.

Best Regards,

David O.

Feb 9 '07 #16
On Feb 8, 6:27 pm, "r.z." <g...@hjkjhk.plwrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};

};

access with data array is convenient for file io and x,y,z for other things
Use overloaded operator [] instead, and given that union in C++ is
also a class - we can have code like this:
class BadIndexException
{
public:
BadIndexException(const char* text = 0)
{
// ...
}
};

union U
{
public:
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
return y;
else if (index == 2)
return z;
else
throw BadIndexException("Invalid index");
};
private:
float data[3];
public:
struct
{
float x;
float y;
float z;
};
};
int main(int argc, char* argv[])
{
U u;
u[0] = 2.0;
u[2] = 3.0;
u.x = 4.0;
printf("u[0]= %f, sizeof(u)= %d, sizeof(float[3])= %d", u[0],
sizeof(u), sizeof(float[3]));
return 0;
}

This code will produce "u[0]= 4.000000, sizeof(u)= 12,
sizeof(float[3])= 12", from which you can see that desired behavior
and savings of memory are achieved.

---
Alexei Polkhanov
Sr. Consultant/Software Systems Analyst
Tel: (604) 719-2515
E-mail: us****@monteaureus.com
http://www.monteaureus.com/

Feb 9 '07 #17
On Feb 10, 10:19 am, "Alexei Polkhanov" <apolkha...@relic.comwrote:
On Feb 8, 6:27 pm, "r.z." <g...@hjkjhk.plwrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};
access with data array is convenient for file io and x,y,z for other things

Use overloaded operator [] instead, and given that union in C++ is
also a class - we can have code like this:
class BadIndexException
{
public:
BadIndexException(const char* text = 0)
{
// ...
}

};

union U
{
public:
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
return y;
else if (index == 2)
return z;
else
throw BadIndexException("Invalid index");
};
private:
float data[3];
public:
struct
{
float x;
float y;
float z;
};

};

int main(int argc, char* argv[])
{
U u;
u[0] = 2.0;
u[2] = 3.0;
u.x = 4.0;
printf("u[0]= %f, sizeof(u)= %d, sizeof(float[3])= %d", u[0],
sizeof(u), sizeof(float[3]));
return 0;

}

This code will produce "u[0]= 4.000000, sizeof(u)= 12,
sizeof(float[3])= 12", from which you can see that desired behavior
and savings of memory are achieved.
Your code doesn't need the private data[] array at all. Change the
union to an ordinary class, remove the private data[] array and you
get the same result. You could then even change the x,y,z members into
regular class data members instead of nesting them inside an anonymous
struct. This would make the class very simple. The result would look
something like this:

class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;

// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};
};

Personally, to me this then becomes the "best compromise" solution to
the original poster's problem. It allows clients to continue using
x,y,z member variables but also access using array index notation. It
even allows array index checking to be done transparently. The one
thing it does not do (to my understanding of the standard) is
guarantee that x,y,z are laid out in memory in an equivalent fashion
to float[3], so if you need that then this solution is probably not
sufficient.

--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia

Feb 10 '07 #18
On Feb 9, 8:00 am, "peter koch" <peter.koch.lar...@gmail.comwrote:
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
r.z. wrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};
access with data array is convenient for file io and x,y,z for other
things
If you mix access modes, you get undefined behavior. However, you can fake
the interface as follows:
struct Vector3 {
float data [3];
float & x;
float & y;
float & z;
Vector3 ( void )
: x ( data[0] )
, y ( data[1] )
, z ( data[2] )
{
x = y = z = 0;
}
};
#include <iostream>
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y << '\n';
}
An obvious drawback is that assignment and copy-construction become tricky.
Also, it is not clear whether the compiler will realize the reference
members as pointers and allocate additional space. Therefore, the following
seems to be better:
struct Vector3 {
float data [3];
float & x ( void ) {
return ( data[0] );
}
float const & x ( void ) const {
return ( data[0] );
}
float & y ( void ) {
return ( data[1] );
}
float const & y ( void ) const {
return ( data[1] );
}
float & z ( void ) {
return ( data[2] );
}
float const & z ( void ) const {
return ( data[2] );
}
};
#include <iostream>
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y() << '\n';
}
Now, there is still the issue that the user could easily do data[4] and
trigger undefined behavior. Thus, maybe:
#include <cassert>
struct Vector3 {
static unsigned int const size = 3;
private:
float data [size];
public:
Vector3 ( void )
: data ()
{}
float & x ( void ) {
return ( data[0] );
}
float const & x ( void ) const {
return ( data[0] );
}
float & y ( void ) {
return ( data[1] );
}
float const & y ( void ) const {
return ( data[1] );
}
float & z ( void ) {
return ( data[2] );
}
float const & z ( void ) const {
return ( data[2] );
}
float & operator[] ( unsigned i ) {
assert( i < size );
return ( data[i] );
}
float const & operator[] ( unsigned i ) const {
assert( i < size );
return ( data[i] );
}
};
#include <iostream>
int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y() << '\n';
}
Finally, you may consider to use double instead of float.
Best
Kai-Uwe Bux- Skjul tekst i anførselstegn -
- Vis tekst i anførselstegn -

Your solution has the nice property that it is portable and does not
break the standard. The problem is that the references will take up
space on many compilers - or at least did when I tried to use the same
trick.
The "union hack" is formally undefined behaviour, but in practice it
is very portable.

/Peter

I'm not sure that it is very portable. I have seen that, depending on
compiler options, members inside a struct can change their relative
position.
I don't think there is anything in the standard that says for the
following x,y and z must have consecutive memory addresses.
union
{
float[3] data;
struct
{
float x, y, z;
};
};

&x == data ??
&y == data + 1??
&z == data + 2 ??

I think the compiler has the freedom to choose any memory addresses,
so we probably can have &x == data + N, 0 < N <= 2

Luis

Feb 10 '07 #19
kalki70 wrote:
On Feb 9, 8:00 am, "peter koch" <peter.koch.lar...@gmail.comwrote:
>On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
r.z. wrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};
access with data array is convenient for file io and x,y,z for other
things
If you mix access modes, you get undefined behavior. However, you can
fake the interface as follows:
struct Vector3 {
float data [3];
float & x;
float & y;
float & z;
Vector3 ( void )
: x ( data[0] )
, y ( data[1] )
, z ( data[2] )
{
x = y = z = 0;
}
};
#include <iostream>
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y << '\n';
}
An obvious drawback is that assignment and copy-construction become
tricky. Also, it is not clear whether the compiler will realize the
reference members as pointers and allocate additional space. Therefore,
the following seems to be better:
struct Vector3 {
float data [3];
float & x ( void ) {
return ( data[0] );
}
float const & x ( void ) const {
return ( data[0] );
}
float & y ( void ) {
return ( data[1] );
}
float const & y ( void ) const {
return ( data[1] );
}
float & z ( void ) {
return ( data[2] );
}
float const & z ( void ) const {
return ( data[2] );
}
};
#include <iostream>
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y() << '\n';
}
Now, there is still the issue that the user could easily do data[4] and
trigger undefined behavior. Thus, maybe:
#include <cassert>
struct Vector3 {
static unsigned int const size = 3;
private:
float data [size];
public:
Vector3 ( void )
: data ()
{}
float & x ( void ) {
return ( data[0] );
}
float const & x ( void ) const {
return ( data[0] );
}
float & y ( void ) {
return ( data[1] );
}
float const & y ( void ) const {
return ( data[1] );
}
float & z ( void ) {
return ( data[2] );
}
float const & z ( void ) const {
return ( data[2] );
}
float & operator[] ( unsigned i ) {
assert( i < size );
return ( data[i] );
}
float const & operator[] ( unsigned i ) const {
assert( i < size );
return ( data[i] );
}
};
#include <iostream>
int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y() << '\n';
}
Finally, you may consider to use double instead of float.
Best
Kai-Uwe Bux- Skjul tekst i anførselstegn -
- Vis tekst i anførselstegn -

Your solution has the nice property that it is portable and does not
break the standard. The problem is that the references will take up
space on many compilers - or at least did when I tried to use the same
trick.
The "union hack" is formally undefined behaviour, but in practice it
is very portable.

/Peter


I'm not sure that it is very portable. I have seen that, depending on
compiler options, members inside a struct can change their relative
position.
That would require an intervening access specifier [9.2/12]:

Nonstatic data members of a (non-union) class declared without an
intervening access-specifier are allocated so that later members have
higher addresses within a class object. The order of allocation of
nonstatic data members separated by an access-specifier is unspecified
(11.1). Implementation alignment requirements might cause two adjacent
members not to be allocated immediately after each other; so might
requirements for space for managing virtual functions (10.3) and virtual
base classes (10.1).
I don't think there is anything in the standard that says for the
following x,y and z must have consecutive memory addresses.
union
{
float[3] data;
struct
{
float x, y, z;
};
};

&x == data ??
&y == data + 1??
&z == data + 2 ??
Well, since the standard mentions alignment requirements as the only reason
for gaps in POD types, it gives a string hint that in the above case, there
are not gaps between the various floats.
>
I think the compiler has the freedom to choose any memory addresses,
so we probably can have &x == data + N, 0 < N <= 2
Since we are actually not talking the standard anymore, but portability from
a practical point of view, I would like to ask whether you know a compiler
that chooses N != 0?
Best

Kai-Uwe Bux
Feb 10 '07 #20
In article <eq**********@nemesis.news.tpi.pl>, gk***@hjkjhk.pl says...
class vector3
{
public:
union
{
float[3] data;
No -- in fact, this isn't even well-formed. Presumably you meant:

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

access with data array is convenient for file io and x,y,z for other things
No -- no padding is allowed between the elements of the array, but it is
allowed between x, y and z. You could make a pretty good case that x and
data[0] can be accessed interchangeably and legitimately, but beyond
that it's likely to break. You could do something on this general order:

struct vector3 {
float data[3];
int &x, &y, &z;

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

or:

struct vector3 {
int x, y, z;

int &operator[](size_t index) {
if (index == 0)
return x;
else if (index == 1)
return y;
else // bounds-check if you see fit.
return z;
}
};

The difference between the two is a potential time/space tradeoff: the
first version may easily make a vector3 twice as big. The second might
make access via operator[] slower.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Feb 10 '07 #21
In article <11**********************@a75g2000cwd.googlegroups .com>,
al******@pobox.com says...
I'm not sure that it is very portable. I have seen that, depending on
compiler options, members inside a struct can change their relative
position.
I don't think there is anything in the standard that says for the
following x,y and z must have consecutive memory addresses.
union
{
float[3] data;
struct
{
float x, y, z;
};
};

&x == data ??
&y == data + 1??
&z == data + 2 ??

I think the compiler has the freedom to choose any memory addresses,
so we probably can have &x == data + N, 0 < N <= 2
Not exactly. The anonymous struct containing x, y and z is an aggregate,
(section 8.5.1) so the elements must be allocated in ascending order.
They could/can be rearranged only if separated by an access specifier.
Padding is allowed betwen them, but not before the first element.

'data' is an array, so the elements must be allocated consecutively (no
padding is allowed before or between elements).

Each element in the union must reside at the same address, so data[0]
and x must have the same address. Due the possibility of padding
before/after y and z, however, their addresses may not be the same as
data[1] and data[2] respectively.

There are a few other details that aren't really right, (e.g. the syntax
of the array definition, lack of a tag on the union so it can't really
be used) but I doubt these are really relevant to the OP's question.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Feb 10 '07 #22
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
>
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;

// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};

};

Personally, to me this then becomes the "best compromise" solution to
the original poster's problem. It allows clients to continue using
x,y,z member variables but also access using array index notation. It
Agree, this solution is much better, however I was under impression
that
using "union" had to be part of the solution since it was part of the
question.

- Alexei.

Feb 13 '07 #23
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:


class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};
};
Personally, to me this then becomes the "best compromise" solution to
the original poster's problem. It allows clients to continue using
x,y,z member variables but also access using array index notation. It

Agree, this solution is much better, however I was under impression
that
using "union" had to be part of the solution since it was part of the
question.

- Alexei.
I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block. The static array of
pointer to members allows a direct access to the variable you need, is
extensible, and doesn not add any space to the class itself, since
it's a class static. As I said, I'm not really sure about the init
time of this array, but given the fact that it's a static POD array,
it should be initialized quite early. Further refinement (throwing a
out_of_range exception) is not difficult to implement (as it only
requires a test against the size of the array, and the mandatory throw
statement). The simplicity of operator[] will make this method a good
candidate for inlining, so in the end the code is quite efficient.

Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.

Regards,

-- Emmanuel Deloget

Feb 14 '07 #24
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};
};
Personally, to me this then becomes the "best compromise" solution to
the original poster's problem. It allows clients to continue using
x,y,z member variables but also access using array index notation. It
Agree, this solution is much better, however I was under impression
that
using "union" had to be part of the solution since it was part of the
question.
- Alexei.

I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block. The static array of
pointer to members allows a direct access to the variable you need, is
extensible, and doesn not add any space to the class itself, since
it's a class static. As I said, I'm not really sure about the init
time of this array, but given the fact that it's a static POD array,
it should be initialized quite early. Further refinement (throwing a
out_of_range exception) is not difficult to implement (as it only
requires a test against the size of the array, and the mandatory throw
statement). The simplicity of operator[] will make this method a good
candidate for inlining, so in the end the code is quite efficient.

Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.
Probably the biggest weakness is that using the static array makes
your class unsuitable for use in a multi-threaded application. If your
app is single threaded, then it's fine.

--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia

Feb 15 '07 #25
I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block.
No need to be reluctant to defend your ideas. A robust discussion is
what makes this list and other like it interesting, not to mention
educational. :)

--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia

Feb 15 '07 #26
Craig Scott wrote:
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote:
>On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};
};
Personally, to me this then becomes the "best compromise" solution to
the original poster's problem. It allows clients to continue using
x,y,z member variables but also access using array index notation. It
Agree, this solution is much better, however I was under impression
that
using "union" had to be part of the solution since it was part of the
question.
- Alexei.

I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block. The static array of
pointer to members allows a direct access to the variable you need, is
extensible, and doesn not add any space to the class itself, since
it's a class static. As I said, I'm not really sure about the init
time of this array, but given the fact that it's a static POD array,
it should be initialized quite early. Further refinement (throwing a
out_of_range exception) is not difficult to implement (as it only
requires a test against the size of the array, and the mandatory throw
statement). The simplicity of operator[] will make this method a good
candidate for inlining, so in the end the code is quite efficient.

Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.

Probably the biggest weakness is that using the static array makes
your class unsuitable for use in a multi-threaded application. If your
app is single threaded, then it's fine.
I am not sure about that.

The proposed solution was essentially this:

struct Vector3 {

float x, y, z;

float const & operator[](unsigned int i) const {
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}

float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y << '\n';
}
The static array is const. It never changes, so apart from initialization
issues, I do not see a problem in multithreading (probably, I am very naive
here:-). If there is a problem, one could make operator[] atomic using some
RAII mutex wrapper so that reads to the static data is properly serialized.
That would take care of the initialization issue, as well. Code could look
like this:

class Vector3 {

static some_lock_type the_lock;

public:

float x, y, z;

float const & operator[](unsigned int i) const {
SomeLockAcquiringWrapper the_guard ( the_lock );
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}

float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}

};

However, it might be to costly to do that :-(
Best

Kai-Uwe Bux
Feb 15 '07 #27
On Feb 15, 1:49 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
Craig Scott wrote:
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};
};
Personally, to me this then becomes the "best compromise" solutionto
the original poster's problem. It allows clients to continue using
x,y,z member variables but also access using array index notation.It
Agree, this solution is much better, however I was under impression
that
using "union" had to be part of the solution since it was part of the
question.
- Alexei.
I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block. The static array of
pointer to members allows a direct access to the variable you need, is
extensible, and doesn not add any space to the class itself, since
it's a class static. As I said, I'm not really sure about the init
time of this array, but given the fact that it's a static POD array,
it should be initialized quite early. Further refinement (throwing a
out_of_range exception) is not difficult to implement (as it only
requires a test against the size of the array, and the mandatory throw
statement). The simplicity of operator[] will make this method a good
candidate for inlining, so in the end the code is quite efficient.
Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.
Probably the biggest weakness is that using the static array makes
your class unsuitable for use in a multi-threaded application. If your
app is single threaded, then it's fine.

I am not sure about that.

The proposed solution was essentially this:

struct Vector3 {

float x, y, z;

float const & operator[](unsigned int i) const {
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}

float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y << '\n';

}

The static array is const. It never changes, so apart from initialization
issues, I do not see a problem in multithreading (probably, I am very naive
here:-). If there is a problem, one could make operator[] atomic using some
RAII mutex wrapper so that reads to the static data is properly serialized.
That would take care of the initialization issue, as well. Code could look
like this:

class Vector3 {

static some_lock_type the_lock;

public:

float x, y, z;

float const & operator[](unsigned int i) const {
SomeLockAcquiringWrapper the_guard ( the_lock );
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}

float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}

};

However, it might be to costly to do that :-(

Your static array is const but you cast away the const-ness with your
non-const operator[], so clients would be free to try to change the
contents with potentially lethal results. The use of locking will, of
course, get you around the data corruption issue, but I doubt clients
of the class would be expecting to pay the penalty of locking just to
access a class like this. I certainly wouldn't.

--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia

Feb 15 '07 #28
Craig Scott wrote:
On Feb 15, 1:49 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
>Craig Scott wrote:
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};
};
Personally, to me this then becomes the "best compromise" solution
to the original poster's problem. It allows clients to continue
using x,y,z member variables but also access using array index
notation. It
Agree, this solution is much better, however I was under impression
that
using "union" had to be part of the solution since it was part of
the question.
- Alexei.
>I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block. The static array of
pointer to members allows a direct access to the variable you need, is
extensible, and doesn not add any space to the class itself, since
it's a class static. As I said, I'm not really sure about the init
time of this array, but given the fact that it's a static POD array,
it should be initialized quite early. Further refinement (throwing a
out_of_range exception) is not difficult to implement (as it only
requires a test against the size of the array, and the mandatory throw
statement). The simplicity of operator[] will make this method a good
candidate for inlining, so in the end the code is quite efficient.
>Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.
Probably the biggest weakness is that using the static array makes
your class unsuitable for use in a multi-threaded application. If your
app is single threaded, then it's fine.

I am not sure about that.

The proposed solution was essentially this:

struct Vector3 {

float x, y, z;

float const & operator[](unsigned int i) const {
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}

float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y << '\n';

}

The static array is const. It never changes, so apart from initialization
issues, I do not see a problem in multithreading (probably, I am very
naive here:-). If there is a problem, one could make operator[] atomic
using some RAII mutex wrapper so that reads to the static data is
properly serialized. That would take care of the initialization issue, as
well. Code could look like this:

class Vector3 {

static some_lock_type the_lock;

public:

float x, y, z;

float const & operator[](unsigned int i) const {
SomeLockAcquiringWrapper the_guard ( the_lock );
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}

float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}

};

However, it might be to costly to do that :-(


Your static array is const but you cast away the const-ness with your
non-const operator[], so clients would be free to try to change the
contents with potentially lethal results.
No, they can't even try: the const_cast is not applied to the static
object "proxy" at any time, it is applied to the object for which the
member function operator[] is called. That does not propagate to static
members. Nobody, at any time, can change the pointer-to-member entries in
proxy.

However, if you feel uncomfortable using the harmless const_cast<trick,
you can easily rewrite the whole thing equivalently as follows:

#include <cassert>
#include <cstddef>

class Vector3 {

static
float Vector3::* const
proxy ( std::size_t i ) {
static float Vector3::* const the_proxy [3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return ( the_proxy[i] );
}

public:

float x, y, z;

float const & operator[] ( std::size_t i ) const {
assert( i < 3 );
return (*this).*(proxy(i));
}

float & operator[] ( std::size_t i ) {
assert( i < 3 );
return (*this).*(proxy(i));
}

};
#include <iostream>

int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y << '\n';
}
The use of locking will, of
course, get you around the data corruption issue, but I doubt clients
of the class would be expecting to pay the penalty of locking just to
access a class like this. I certainly wouldn't.
I still don't see any data corruption issue. Could you present a scenario,
in code, where it might happen.
Best

Kai-Uwe Bux
Feb 15 '07 #29
On Feb 14, 4:04 pm, "Emmanuel Deloget" <log...@free.frwrote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:

[snip]
>
I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block.
I have to agree with you: that idea is very nice. If I had to choose
anything but the dirty hack, your solutioon is by far the most elegant
and the cleanest way out. The only problem I have with your solution
(and this is valid for all the non-hacks) is the overhead which I
guess would be present also for the best optimising compilers.
[snip]
Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.
Only the potential multithreading problem which should be easily
solvable.

/Peter

Feb 15 '07 #30
On 15 fév, 03:14, "Craig Scott" <audiofana...@gmail.comwrote:
>
Probably the biggest weakness is that using the static array makes
your class unsuitable for use in a multi-threaded application. If your
app is single threaded, then it's fine.

--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia
No, this is not a problem here. The static array is never modified -
it is private to vector3d and is not modified by any member of this
class. It means that you don't have to worry about using it in a
multithreaded application - you can view it as yet another global
class static constant.

To you defense, I should have made the code const correct. Here is the
corrected code snippet:

class vector3d
{
static float vector3d::* const proxy[3];
public:
float x, y, z;
float& operator[](unsigned int i) { return (*this).*proxy[i]; }
};

float vector3d::* const vector3d::proxy[3] =
{ &vector3d::x, &vector3d::y, &vector3d::z };

int main()
{
vector3d v;
v[0] = 1.0f;
}

Now, the "proxy" static array is immutable - so it won't give you
headaches if you use it in a multi-threaded environment.

Regards,

-- Emmanuel Deloget

Feb 16 '07 #31
On Feb 16, 10:23 pm, "Emmanuel Deloget" <log...@free.frwrote:
On 15 fév, 03:14, "Craig Scott" <audiofana...@gmail.comwrote:
Probably the biggest weakness is that using the static array makes
your class unsuitable for use in a multi-threaded application. If your
app is single threaded, then it's fine.
--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia

No, this is not a problem here. The static array is never modified -
it is private to vector3d and is not modified by any member of this
class. It means that you don't have to worry about using it in a
multithreaded application - you can view it as yet another global
class static constant.

To you defense, I should have made the code const correct. Here is the
corrected code snippet:

class vector3d
{
static float vector3d::* const proxy[3];
public:
float x, y, z;
float& operator[](unsigned int i) { return (*this).*proxy[i]; }

};

float vector3d::* const vector3d::proxy[3] =
{ &vector3d::x, &vector3d::y, &vector3d::z };

int main()
{
vector3d v;
v[0] = 1.0f;

}

Now, the "proxy" static array is immutable - so it won't give you
headaches if you use it in a multi-threaded environment.

Regards,

-- Emmanuel Deloget
Apologies required! Sorry, I am guilty of not reading your code
closely enough. Yes, your solution is as safe in a multi-threaded
application as any other code (ie the static proxy is not a problem
from a multi-threaded point of view, contrary to my earlier post).

One potential weakness still concerns me though. The actual
initialization of the static array would probably need to be done
outside of the header, otherwise there will be a problem with multiple
instances of the array. That means the compiler would not know that
the operator[] call would always return x, y or z. Hence, a function
call would always be required whenever the operator[] was used,
whereas it would seem reasonable for a modest proportion of people to
expect that operator[] should not be forced to pay this penalty for a
class such as this. Rather, they would probably expect that compilers
should be able to optimize it all away to a direct access to the
underlying data (ie inline everything to do with the call). Apart from
this though, the idea does seem to be quite elegant.

--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia

Feb 17 '07 #32
On Feb 15, 7:01 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
Craig Scott wrote:
On Feb 15, 1:49 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote:
Craig Scott wrote:
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
// operator[] should be public
public:
// Should also provide a const version of this
float& operator[](int index)
{
if (index == 0)
return x;
else if (index == 1)
y;
else if (index == 2)
return z;
else
// Throw exception or something else appropriate
};
};
Personally, to me this then becomes the "best compromise" solution
to the original poster's problem. It allows clients to continue
using x,y,z member variables but also access using array index
notation. It
Agree, this solution is much better, however I was under impression
that
using "union" had to be part of the solution since it was part of
the question.
- Alexei.
I (really) dislike being the one who says "there is a better solution,
look at mine", but that's going to be what I'm going to do - as the
solution I proposed is quite elegant, standard-proof, and doesn't
require any (endless?) if/then/if/then/else block. The static arrayof
pointer to members allows a direct access to the variable you need,is
extensible, and doesn not add any space to the class itself, since
it's a class static. As I said, I'm not really sure about the init
time of this array, but given the fact that it's a static POD array,
it should be initialized quite early. Further refinement (throwing a
out_of_range exception) is not difficult to implement (as it only
requires a test against the size of the array, and the mandatory throw
statement). The simplicity of operator[] will make this method a good
candidate for inlining, so in the end the code is quite efficient.
Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.
Probably the biggest weakness is that using the static array makes
your class unsuitable for use in a multi-threaded application. If your
app is single threaded, then it's fine.
I am not sure about that.
The proposed solution was essentially this:
struct Vector3 {
float x, y, z;
float const & operator[](unsigned int i) const {
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}
float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}
};
#include <iostream>
int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y << '\n';
}
The static array is const. It never changes, so apart from initialization
issues, I do not see a problem in multithreading (probably, I am very
naive here:-). If there is a problem, one could make operator[] atomic
using some RAII mutex wrapper so that reads to the static data is
properly serialized. That would take care of the initialization issue,as
well. Code could look like this:
class Vector3 {
static some_lock_type the_lock;
public:
float x, y, z;
float const & operator[](unsigned int i) const {
SomeLockAcquiringWrapper the_guard ( the_lock );
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}
float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}
};
However, it might be to costly to do that :-(
Your static array is const but you cast away the const-ness with your
non-const operator[], so clients would be free to try to change the
contents with potentially lethal results.

No, they can't even try: the const_cast is not applied to the static
object "proxy" at any time, it is applied to the object for which the
member function operator[] is called. That does not propagate to static
members. Nobody, at any time, can change the pointer-to-member entries in
proxy.

However, if you feel uncomfortable using the harmless const_cast<trick,
you can easily rewrite the whole thing equivalently as follows:

#include <cassert>
#include <cstddef>

class Vector3 {

static
float Vector3::* const
proxy ( std::size_t i ) {
static float Vector3::* const the_proxy [3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return ( the_proxy[i] );
}

public:

float x, y, z;

float const & operator[] ( std::size_t i ) const {
assert( i < 3 );
return (*this).*(proxy(i));
}

float & operator[] ( std::size_t i ) {
assert( i < 3 );
return (*this).*(proxy(i));
}

};

#include <iostream>

int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y << '\n';

}
The use of locking will, of
course, get you around the data corruption issue, but I doubt clients
of the class would be expecting to pay the penalty of locking just to
access a class like this. I certainly wouldn't.

I still don't see any data corruption issue. Could you present a scenario,
in code, where it might happen.

Best

Kai-Uwe Bux
Ah, thanks for your patience. I had not properly read the previous
postings and was incorrect in my statements. See my other recent post
in this topic for remaining niggles with this otherwise elegant
solution. :)

--
Computational Fluid Dynamics, CSIRO (CMIS)
Melbourne, Australia

Feb 17 '07 #33

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

20
by: Matthias | last post by:
Hello, I am missing certain functionality of std::string, so I am currently writing some helper functions which operate on strings. On of them is as follows (it's actually two functions): ...
25
by: Joakim Hove | last post by:
Hello, I have code which makses use of variables of type size_t. The code is originally developed on a 32 bit machine, but now it is run on both a 32 bit and a 64 bit machine. In the code...
131
by: pemo | last post by:
Is C really portable? And, apologies, but this is possibly a little OT? In c.l.c we often see 'not portable' comments, but I wonder just how portable C apps really are. I don't write...
28
by: lovecreatesbeauty | last post by:
On gcc, which version of C standard has the most compliant: -c89, -ansi or c99? For more portable C code, which options should be applied to compilation? Can the following options guarantee the...
14
by: Kristan Dyson | last post by:
I was just wondering whether you knew whether it is possible to compile a fully portable C program? I want to write a 'C' CGI program on Windows, then just upload it to my Plus.Net Debian-based...
69
by: Bill Reid | last post by:
This is how I handle a check that the last character of a text file is a newline: /* checks if newline is last character of text file */ unsigned check_text_file_newline_termination(FILE...
23
by: asit | last post by:
what is the difference between portable C, posix C and windows C ???
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.