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 | | | | re: is this portable, conforming to standard, elegant?
"r.z." <gklkj@hjkjhk.plwrote in news:eqgmcp$d7q$1@nemesis.news.tpi.pl: Quote:
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? | | | | re: is this portable, conforming to standard, elegant?
r.z. wrote: Quote:
class vector3
{
public:
union
{
float[3] data;
syntax error.
float data[3]; Quote:
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. | | | | re: is this portable, conforming to standard, elegant?
Is it possible that the structure may be byte-packed in a different manner do you ask whether data[] will always contain values x, y, z in this order?
(like in the structure) then the answer is yes. | | | | re: is this portable, conforming to standard, elegant?
> Quote:
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 :/ | | | | re: is this portable, conforming to standard, elegant?
Depends on how you use it. Behavior is only defined if you access the Quote:
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? | | | | re: is this portable, conforming to standard, elegant?
r.z. wrote: Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
r.z. wrote: Quote: Quote:
>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 | | | | re: is this portable, conforming to standard, elegant?
red floyd a écrit : Quote:
r.z. wrote: Quote:
>class vector3
>{
> public:
> union
> {
> float[3] data;
syntax error.
float data[3]; Quote:
> 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 | | | | re: is this portable, conforming to standard, elegant?
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote: Quote:
r.z. wrote: Quote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};
> Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
On 9 Feb., 03:27, "r.z." <g...@hjkjhk.plwrote: Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
On 9 fév, 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote: Quote:
[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 | | | | re: is this portable, conforming to standard, elegant?
"r.z." <gklkj@hjkjhk.plwrote in message
news:eqgmcp$d7q$1@nemesis.news.tpi.pl... Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
"peter koch" <peter.koch.larsen@gmail.comwrote in message
news:1171018831.067160.76260@m58g2000cwm.googlegro ups.com...
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote: *snap* Quote:
>
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 | | | | re: is this portable, conforming to standard, elegant?
Sylvester Hesp a écrit : Quote:
"peter koch" <peter.koch.larsen@gmail.comwrote in message
news:1171018831.067160.76260@m58g2000cwm.googlegro ups.com...
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
*snap* Quote:
>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 | | | | re: is this portable, conforming to standard, elegant?
On Feb 9, 7:16 am, "Sylvester Hesp" <s.h...@oisyn.nlwrote: Quote:
"peter koch" <peter.koch.lar...@gmail.comwrote in message
>
news:1171018831.067160.76260@m58g2000cwm.googlegro ups.com...
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
>
>
>
*snap*
> Quote:
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 ;)
>
- 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. | | | | re: is this portable, conforming to standard, elegant?
On Feb 8, 6:27 pm, "r.z." <g...@hjkjhk.plwrote: Quote:
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: usenet@monteaureus.com http://www.monteaureus.com/ | | | | re: is this portable, conforming to standard, elegant?
On Feb 10, 10:19 am, "Alexei Polkhanov" <apolkha...@relic.comwrote: Quote:
On Feb 8, 6:27 pm, "r.z." <g...@hjkjhk.plwrote:
> Quote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
> > Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
On Feb 9, 8:00 am, "peter koch" <peter.koch.lar...@gmail.comwrote: Quote:
On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
>
>
> Quote:
r.z. wrote: Quote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};
> Quote: Quote:
access with data array is convenient for file io and x,y,z for other
things
> Quote:
If you mix access modes, you get undefined behavior. However, you can fake
the interface as follows:
> > Quote:
float data [3];
float & x;
float & y;
float & z;
> Quote:
Vector3 ( void )
: x ( data[0] )
, y ( data[1] )
, z ( data[2] )
{
x = y = z = 0;
}
> > Quote:
#include <iostream>
> Quote:
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y << '\n';
> > Quote:
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:
> > > Quote:
float & x ( void ) {
return ( data[0] );
}
> Quote:
float const & x ( void ) const {
return ( data[0] );
}
> Quote:
float & y ( void ) {
return ( data[1] );
}
> Quote:
float const & y ( void ) const {
return ( data[1] );
}
> Quote:
float & z ( void ) {
return ( data[2] );
}
> Quote:
float const & z ( void ) const {
return ( data[2] );
}
> > Quote:
#include <iostream>
> Quote:
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y() << '\n';
> > Quote:
Now, there is still the issue that the user could easily do data[4] and
trigger undefined behavior. Thus, maybe:
> Quote:
#include <cassert>
> > Quote:
static unsigned int const size = 3;
> > Quote:
float data [size];
> > Quote:
Vector3 ( void )
: data ()
{}
> Quote:
float & x ( void ) {
return ( data[0] );
}
> Quote:
float const & x ( void ) const {
return ( data[0] );
}
> Quote:
float & y ( void ) {
return ( data[1] );
}
> Quote:
float const & y ( void ) const {
return ( data[1] );
}
> Quote:
float & z ( void ) {
return ( data[2] );
}
> Quote:
float const & z ( void ) const {
return ( data[2] );
}
> Quote:
float & operator[] ( unsigned i ) {
assert( i < size );
return ( data[i] );
}
> Quote:
float const & operator[] ( unsigned i ) const {
assert( i < size );
return ( data[i] );
}
> > Quote:
#include <iostream>
> Quote:
int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y() << '\n';
> > Quote:
Finally, you may consider to use double instead of float.
> > Quote:
Kai-Uwe Bux- Skjul tekst i anførselstegn -
> Quote:
- 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 | | | | re: is this portable, conforming to standard, elegant?
kalki70 wrote: Quote:
On Feb 9, 8:00 am, "peter koch" <peter.koch.lar...@gmail.comwrote: Quote:
>On 9 Feb., 04:04, Kai-Uwe Bux <jkherci...@gmx.netwrote:
>>
>>
>> Quote:
r.z. wrote:
class vector3
{
public:
union
{
float[3] data;
struct
{
float x, y, z;
};
};
};
>> Quote:
access with data array is convenient for file io and x,y,z for other
things
>> Quote:
If you mix access modes, you get undefined behavior. However, you can
fake the interface as follows:
>> >> Quote:
float data [3];
float & x;
float & y;
float & z;
>> Quote:
Vector3 ( void )
: x ( data[0] )
, y ( data[1] )
, z ( data[2] )
{
x = y = z = 0;
}
>> >> Quote:
#include <iostream>
>> Quote:
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y << '\n';
>> >> Quote:
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:
>> >> >> Quote:
float & x ( void ) {
return ( data[0] );
}
>> Quote:
float const & x ( void ) const {
return ( data[0] );
}
>> Quote:
float & y ( void ) {
return ( data[1] );
}
>> Quote:
float const & y ( void ) const {
return ( data[1] );
}
>> Quote:
float & z ( void ) {
return ( data[2] );
}
>> Quote:
float const & z ( void ) const {
return ( data[2] );
}
>> >> Quote:
#include <iostream>
>> Quote:
int main ( void ) {
Vector3 a;
a.data[1] = 2;
std::cout << a.y() << '\n';
>> >> Quote:
Now, there is still the issue that the user could easily do data[4] and
trigger undefined behavior. Thus, maybe:
>> Quote:
#include <cassert>
>> >> Quote:
static unsigned int const size = 3;
>> >> Quote:
float data [size];
>> >> Quote:
Vector3 ( void )
: data ()
{}
>> Quote:
float & x ( void ) {
return ( data[0] );
}
>> Quote:
float const & x ( void ) const {
return ( data[0] );
}
>> Quote:
float & y ( void ) {
return ( data[1] );
}
>> Quote:
float const & y ( void ) const {
return ( data[1] );
}
>> Quote:
float & z ( void ) {
return ( data[2] );
}
>> Quote:
float const & z ( void ) const {
return ( data[2] );
}
>> Quote:
float & operator[] ( unsigned i ) {
assert( i < size );
return ( data[i] );
}
>> Quote:
float const & operator[] ( unsigned i ) const {
assert( i < size );
return ( data[i] );
}
>> >> Quote:
#include <iostream>
>> Quote:
int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y() << '\n';
>> >> Quote:
Finally, you may consider to use double instead of float.
>> >> Quote:
Kai-Uwe Bux- Skjul tekst i anførselstegn -
>> Quote:
- 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). Quote:
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. Quote:
>
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 | | | | re: is this portable, conforming to standard, elegant?
In article <eqgmcp$d7q$1@nemesis.news.tpi.pl>, gklkj@hjkjhk.pl says... Quote:
class vector3
{
public:
union
{
float[3] data;
No -- in fact, this isn't even well-formed. Presumably you meant:
float data[3]; Quote:
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. | | | | re: is this portable, conforming to standard, elegant?
In article <1171084339.864294.205860@a75g2000cwd.googlegroups .com>, albinet6@pobox.com says... Quote:
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. | | | | re: is this portable, conforming to standard, elegant?
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote: Quote:
>
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. | | | | re: is this portable, conforming to standard, elegant?
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote: Quote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
>
>
>
>
> Quote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
> Quote:
// 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
};
> > Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote: Quote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
>
>
> Quote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
> Quote: Quote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
> Quote: Quote:
// 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
};
> > Quote: Quote:
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
> Quote:
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.
> >
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 | | | | re: is this portable, conforming to standard, elegant?
I (really) dislike being the one who says "there is a better solution, Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
Craig Scott wrote: Quote:
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote: Quote:
>On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
>>
>>
>> Quote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
>> Quote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
>> Quote:
// 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
};
>> >> Quote:
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
>> Quote:
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.
>> >>
>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 | | | | re: is this portable, conforming to standard, elegant?
On Feb 15, 1:49 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote: Quote:
Craig Scott wrote: Quote:
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote: Quote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
> Quote: Quote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
> Quote: Quote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
> Quote: Quote:
// 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
};
> > Quote: Quote:
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
> Quote: Quote:
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.
> > Quote: Quote:
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.
> Quote: Quote:
Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.
> Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
Craig Scott wrote: Quote:
On Feb 15, 1:49 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote: Quote:
>Craig Scott wrote: Quote:
On Feb 15, 2:04 am, "Emmanuel Deloget" <log...@free.frwrote:
>On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
>> Quote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
>> Quote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
>> Quote:
// 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
};
>> >> Quote:
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
>> Quote:
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.
>> >> Quote:
>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.
>> Quote:
>Now, there may be some problems I haven't seen (I don't see every
>problem) - if you find some, please tell me.
>> Quote:
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';
} Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
On Feb 14, 4:04 pm, "Emmanuel Deloget" <log...@free.frwrote: Quote:
On 13 fév, 20:00, "Alexei Polkhanov" <apolkha...@relic.comwrote:
>
>
>
>
[snip] Quote:
>
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] Quote:
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 | | | | re: is this portable, conforming to standard, elegant?
On 15 fév, 03:14, "Craig Scott" <audiofana...@gmail.comwrote: Quote:
>
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 | | | | re: is this portable, conforming to standard, elegant?
On Feb 16, 10:23 pm, "Emmanuel Deloget" <log...@free.frwrote: Quote:
On 15 fév, 03:14, "Craig Scott" <audiofana...@gmail.comwrote:
>
>
> Quote:
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.
> Quote:
--
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 | | | | re: is this portable, conforming to standard, elegant?
On Feb 15, 7:01 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote: Quote:
Craig Scott wrote: Quote:
On Feb 15, 1:49 pm, Kai-Uwe Bux <jkherci...@gmx.netwrote: Quote:
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:
> Quote: Quote:
On Feb 9, 5:05 pm, "Craig Scott" <audiofana...@gmail.comwrote:
> Quote: Quote:
class U
{
// Normally, x,y,z would be private, but OP needs them
// to be public
public:
float x;
float y;
float z;
> Quote: Quote:
// 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
};
> > Quote: Quote:
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
> Quote: Quote:
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.
> > Quote: Quote:
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.
> Quote: Quote:
Now, there may be some problems I haven't seen (I don't see every
problem) - if you find some, please tell me.
> Quote: Quote:
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.
> Quote: Quote:
I am not sure about that.
> Quote: Quote:
The proposed solution was essentially this:
> > > Quote: Quote:
float const & operator[](unsigned int i) const {
static float Vector3::* const proxy[3] =
{ &Vector3::x, &Vector3::y, &Vector3::z };
return (*this).*proxy[i];
}
> Quote: Quote:
float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}
> > Quote: Quote:
#include <iostream>
> Quote: Quote:
int main ( void ) {
Vector3 a;
a[1] = 2;
std::cout << a.y << '\n';
> > Quote: Quote:
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:
> > Quote: Quote:
static some_lock_type the_lock;
> > > Quote: Quote:
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];
}
> Quote: Quote:
float & operator[] ( unsigned int i ) {
return
( const_cast<float&>
( const_cast<Vector3 const *>(this)->operator[](i) ) );
}
> > Quote: Quote:
However, it might be to costly to do that :-(
> Quote:
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';
>
} Quote:
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 |  | | | | /bytes/about
We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights.
Get the best answers to your questions from over 226,471 network members.
|