is this portable

  • Thread starter Martin Vorbrodt
  • Start date
M

Martin Vorbrodt

Hi there. Is this code portable between platforms? Is it also 100% standard
compliant?

#include <iostream>
using namespace std;

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const
{ return (&_x)[c]; }

private:
int _x, _y, _z;
};

int main() {
Point p(1, 2, 3);

cout << p[Point::X] << endl;
cout << p[Point::Y] << endl;
cout << p[Point::Z] << endl;
}

Is it save to get the address of the first member, and do pointer arithmetic
on it to get to all 3 elements?

Thanx
Martin
 
J

John Harrison

Martin Vorbrodt said:
Hi there. Is this code portable between platforms? Is it also 100%
standard
compliant?

No, and no.

But why would you want to write code like this?

john
 
P

Phlip

John said:
Martin Vorbrodt wrote:

No, and no.

The data members were within one private: tag, so their order is
well-defined, and their paddings are implementation-defined.

The second rule makes them non-portable.

So, John, why are they not 100% standard compliant?
But why would you want to write code like this?

Because OpenGL rewards you to. It permits many variations of its methods to
take an array of indices as a primitive "point object".

The OP is advised to assert() that the size of Point equals the size of
three ints, and keep going. Unless if John can talk him out of it.
 
A

Alf P. Steinbach

* Phlip:
So, John, why are they not 100% standard compliant?

The program has undefined effect due to invalid pointer arithmetic
(via indexing). My experience is that you'll probably respond to
that by demanding some further justification. And to respond to
that response in advance, look up the rules for valid pointer values.

Because OpenGL rewards you to. It permits many variations of its methods to
take an array of indices as a primitive "point object".

The OP is advised to assert() that the size of Point equals the size of
three ints, and keep going. Unless if John can talk him out of it.

That's very bad advice because undefined effect is unnecessary.
 
J

John Harrison

Phlip said:
The data members were within one private: tag, so their order is
well-defined, and their paddings are implementation-defined.

The second rule makes them non-portable.

So, John, why are they not 100% standard compliant?

I guess it depends what you mean by standard compliant. I said no because
dubious pointer arithmetic in the Point class potentially allows access to
padding bytes resulting in undefined behaviour.

Because OpenGL rewards you to. It permits many variations of its methods
to
take an array of indices as a primitive "point object".

I can't see the advantage of the OP's code over this, which is portable and
standards compliant

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z)
{
_val[X] = x;
_val[Y] = y;
_val[Z] = z;
}

const int& operator[](COORDS c) const
{ return _val[c]; }

private:
int _val[3];
};
The OP is advised to assert() that the size of Point equals the size of
three ints, and keep going. Unless if John can talk him out of it.

The OP's code is very likely to work in practice, but it seems to me that
100% complaint code could do as well, and should be preferred.

john
 
T

Thomas Matthews

John said:
John Harrison wrote:




The data members were within one private: tag, so their order is
well-defined, and their paddings are implementation-defined.

The second rule makes them non-portable.

So, John, why are they not 100% standard compliant?


I guess it depends what you mean by standard compliant. I said no because
dubious pointer arithmetic in the Point class potentially allows access to
padding bytes resulting in undefined behaviour.


Because OpenGL rewards you to. It permits many variations of its methods
to
take an array of indices as a primitive "point object".


I can't see the advantage of the OP's code over this, which is portable and
standards compliant

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z)
{
_val[X] = x;
_val[Y] = y;
_val[Z] = z;
}

const int& operator[](COORDS c) const
{ return _val[c]; }

private:
int _val[3];
};

The OP is advised to assert() that the size of Point equals the size of
three ints, and keep going. Unless if John can talk him out of it.


The OP's code is very likely to work in practice, but it seems to me that
100% complaint code could do as well, and should be preferred.

john

I thought identifiers with leading underscores were reserved
for the compiler / implementation's usage. If this is so,
then it is not portable (because some compilers may have
identifiers with those names).

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.comeaucomputing.com/learn/faq/
Other sites:
http://www.josuttis.com -- C++ STL Library book
http://www.sgi.com/tech/stl -- Standard Template Library
 
J

John Harrison

I thought identifiers with leading underscores were reserved
for the compiler / implementation's usage. If this is so,
then it is not portable (because some compilers may have
identifiers with those names).

Only when those identifiers are at namespace scope. Within a class or a
function they are OK.

john
 
M

Mike Wahler

Thomas Matthews said:
John said:
John Harrison wrote:


Martin Vorbrodt wrote:

Hi there. Is this code portable between platforms? Is it also 100%
standard
compliant?

No, and no.

The data members were within one private: tag, so their order is
well-defined, and their paddings are implementation-defined.

The second rule makes them non-portable.

So, John, why are they not 100% standard compliant?


I guess it depends what you mean by standard compliant. I said no because
dubious pointer arithmetic in the Point class potentially allows access to
padding bytes resulting in undefined behaviour.


But why would you want to write code like this?

Because OpenGL rewards you to. It permits many variations of its methods
to
take an array of indices as a primitive "point object".


I can't see the advantage of the OP's code over this, which is portable and
standards compliant

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z)
{
_val[X] = x;
_val[Y] = y;
_val[Z] = z;
}

const int& operator[](COORDS c) const
{ return _val[c]; }

private:
int _val[3];
};

The OP is advised to assert() that the size of Point equals the size of
three ints, and keep going. Unless if John can talk him out of it.


The OP's code is very likely to work in practice, but it seems to me that
100% complaint code could do as well, and should be preferred.

john

I thought identifiers with leading underscores were reserved
for the compiler / implementation's usage.

Only those at namespace or 'global' scope.

However, I've adopted the practice of not using them at
all, obviating the need to even consider the details of
rules like this.

-Mike
 
P

Phlip

John said:
I guess it depends what you mean by standard compliant. I said no because
dubious pointer arithmetic in the Point class potentially allows access to
padding bytes resulting in undefined behaviour.

That's implementation-defined. But I have not read the other posts yet...
I can't see the advantage of the OP's code over this, which is portable and
standards compliant

I added the syntactic sugar the OP wanted:
class Point {
public:
enum COORDS { X = 0, Y, Z };

int &x_;
int &y_;
int &z_;
Point(int x, int y, int z)
: x_(_val[X]),
y_(_val[Y]),
z_(_val[Z])
{
_val[X] = x;
_val[Y] = y;
_val[Z] = z;
}

const int& operator[](COORDS c) const
{ return _val[c]; }

private:
int _val[3];
};

New question: Is taking a reference to the storage where a variable will be
before it initializes defined?
 
P

Phlip

Alf said:
The program has undefined effect due to invalid pointer arithmetic
(via indexing). My experience is that you'll probably respond to
that by demanding some further justification. And to respond to
that response in advance, look up the rules for valid pointer values.

Point to a valid object, NULL, or one off the end of an array..? Nope - not
gonna ask.

(I'm still waiting to hear if you found a real reason to disliked my TEST_
macro, besides I wrote it.)
That's very bad advice because undefined effect is unnecessary.

Code dealing with Points, such as OpenGL code, typically undergoes sick
optimizations. Naturally I have seen worse than my bad advice.

Even applying my other post might degrade performance, when the compiler
can't optimize the references away fully.

Oh, and would those references make the Point a non-PODs? So the pointer
trick might have another reason to be less defined?
 
M

Mike Wahler

Phlip said:
That's implementation-defined.

Which renders it unportable.
But I have not read the other posts yet...
I can't see the advantage of the OP's code over this, which is portable and
standards compliant

I added the syntactic sugar the OP wanted:
class Point {
public:
enum COORDS { X = 0, Y, Z };

int &x_;
int &y_;
int &z_;
Point(int x, int y, int z)
: x_(_val[X]),
y_(_val[Y]),
z_(_val[Z])
{
_val[X] = x;
_val[Y] = y;
_val[Z] = z;
}

const int& operator[](COORDS c) const
{ return _val[c]; }

private:
int _val[3];
};

New question: Is taking a reference to the storage where a variable will be
before it initializes defined?

AFAIK, it's perfectly legal, just don't try to evaluate the
object to which it refers until that object has been assigned
a value. IMO this is the same thing as declaring a reference
parameter to a function, which of course is fine.

-Mike
 
P

Phlip

Mike said:
Which renders it unportable.

Sorry - I didn't hear "portable to any conceivable C++ platform". Hence the
assert(sizeof) thing to simply raise an alarm when the portability envelop
is crossed. But there I go again, helping solve the outer problem instead of
the exact question...

The OP is advised to write code whose legality can be trivially determined,
and to profile various kinds of statements to learn which ones are optimal.
 
A

Alf P. Steinbach

* Phlip:
(I'm still waiting to hear if you found a real reason to disliked my TEST_
macro, besides I wrote it.)

Well, I dislike macros, so in the context of what would be _ideal_ I dislike
macro-based code -- but in the context of getting the job done, if it works,
don't fix it... ;-) There's also the question of learning curve both for the
one who implements the thing (that doesn't apply to you here, but in general)
and for those using it. So in that non-C++ perspective I think it's fine, a
Good Idea (TM), because it's much better to have automated testing than not.
 
J

John Harrison

Phlip said:
That's implementation-defined. But I have not read the other posts yet...

OK my bad, I didn't look it up.
I can't see the advantage of the OP's code over this, which is portable and
standards compliant

I added the syntactic sugar the OP wanted:
class Point {
public:
enum COORDS { X = 0, Y, Z };

int &x_;
int &y_;
int &z_;
Point(int x, int y, int z)
: x_(_val[X]),
y_(_val[Y]),
z_(_val[Z])
{
_val[X] = x;
_val[Y] = y;
_val[Z] = z;
}

const int& operator[](COORDS c) const
{ return _val[c]; }

private:
int _val[3];
};

Are you sure? In his original post he as _x, _y and _z as private.

In any case I don't consider the price worth paying.

john
 
M

Mike Wahler

Phlip said:
Sorry - I didn't hear "portable to any conceivable C++ platform".

I heard "is this portable?" and "is this 100% standard compliant?"
Hence the
assert(sizeof) thing to simply raise an alarm when the portability envelop
is crossed. But there I go again, helping solve the outer problem instead of
the exact question...

If what you wrote helps anyone, that's great.
The OP is advised to write code whose legality can be trivially determined,
and to profile various kinds of statements to learn which ones are
optimal.

Agreed.

-Mike
 
A

Alf P. Steinbach

* Martin Vorbrodt:
Hi there. Is this code portable between platforms? Is it also 100% standard
compliant?

#include <iostream>
using namespace std;

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const
{ return (&_x)[c]; }

No (because of padding & alignment issues), and no (it's Undefined Behavior).
 
C

Chris Uzdavinis

Martin Vorbrodt said:
Hi there. Is this code portable between platforms? Is it also 100% standard
compliant? ....

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const
{ return (&_x)[c]; }

private:
int _x, _y, _z;
};

No. Absolutely not. The address of _x tells you where _x is located.
It does not imply anything about the other members, and it's
undefined to use &x as the base pointer of an array, since it doesn't
point into one. You really don't want to use it as if it were one,
because that takes you into undefined territory.

If you do this, you're depending on compiler implementation detail
such as the order that the members are layed out in memory, and that
there's no padding bytes between the members. It goes without saying
that depending on compiler implementation details is not portable.
Is it save to get the address of the first member, and do pointer arithmetic
on it to get to all 3 elements?

Tricks like this may sometimes (unfortunately) work as expected, and
sometimes even so-called experts do them. But undefined behavior is
undefined behavior, and the appearance of "working" is one way
undefined behvaior manifests itself. This kind of coding is
needlessly dangerous and certain to eventually cause you harm. (That
is, it WILL break someday, on some platform, and you'll spend hours
debugging a problem that should not exist.) I suggest you write code
that is so obviously correct that you don't have to ask. For example:

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const
{ switch(c) {
case X: return _x;
case Y: return _y;
case Z: return _z;
default: throw std::runtime_error("Invalid argument");
}

private:
int _x, _y, _z;
};

Or alternately,

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) { xyz[X] = x; xyz[Y] = y; xyz[Z]
= z;}

const int& operator[](COORDS c) const
{ return xyz[c]; }

private:
int xyz[3];
};

In this second case, member xyz is an array and it's thereforce safe
to index into. (Assuming the argument to operator[] is in range, of
course.)
 
M

Maciej Sobczak

Hi,

Martin said:
Hi there. Is this code portable between platforms? Is it also 100% standard
compliant?

#include <iostream>
using namespace std;

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const
{ return (&_x)[c]; }

private:
int _x, _y, _z;
};

int main() {
Point p(1, 2, 3);

cout << p[Point::X] << endl;
cout << p[Point::Y] << endl;
cout << p[Point::Z] << endl;
}

Is it save to get the address of the first member, and do pointer arithmetic
on it to get to all 3 elements?

No, it is no safe. There may be padding between the members and your
indexing operator assumes that the members are placed one after another
in memory. This is not guaranteed (but may happen to work on many
platforms).

What about this:

class Point
{
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z)
{ coords_[0] = x; coords_[1] = y; coords_[2] = z;}

const int & operator[](COORDS c) const { return coords_[c]; }

private:
int coords_[3];
};
 
T

Tokyo Tomy

Martin Vorbrodt said:
Hi there. Is this code portable between platforms? Is it also 100% standard
compliant?

...
Is it save to get the address of the first member, and do pointer arithmetic
on it to get to all 3 elements?

Pointer arithmetic is effective for continuous memory arrangement. I
don't think that the standard say something how to arrange class
members, _x, _y, and _z, on memory. Therefore your code is not
guaranteed to work, although I think it works in many cases.
 
C

Carl Barron

Martin Vorbrodt said:
Hi there. Is this code portable between platforms? Is it also 100% standard
compliant?

#include <iostream>
using namespace std;

class Point {
public:
enum COORDS { X = 0, Y, Z };
Point(int x, int y, int z) : _x(x), _y(y), _z(z) {}

const int& operator[](COORDS c) const
{ return (&_x)[c]; }

private:
int _x, _y, _z;
};

int main() {
Point p(1, 2, 3);

cout << p[Point::X] << endl;
cout << p[Point::Y] << endl;
cout << p[Point::Z] << endl;
}

Is it save to get the address of the first member, and do pointer arithmetic
on it to get to all 3 elements?
I can't name a compiler on which it will fail, but the compiler is
permitted to insert padding of its choice between _x,_y,_z and is
not permitted to do so with an int array. That said why not just provide
three access inlined access function members??

class Point
{
// ...
public
int X() const {return _x;}
int Y() const {return _y;}
int Z() const {return _z;}
};

This is portable and unless you prohibit inliniing it willl be at least
as efficient.
Point p(1,2,3);
std::cout << p.X() << ',' << p.Y() << ',' << p.Z() << '\n';
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,202
Latest member
MikoOslo

Latest Threads

Top