Valid C++?

  • Thread starter andrew queisser
  • Start date
M

mlimber

Victor said:
mlimber said:
andrew said:
Is this code below valid C++? I'd like to use this construct but I'm
not sure if it'll be portable.

struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];
};

Though that seems to work, for the sake of clarity, I would do it
differently. At worst, something like this:

char sameSizeAsFooX[ sizeof foo().x ];

This requires 'foo' to be default-constructible, which is not always
possible.
Better, IMHO, would be:

struct foo
{
enum { SIZE = 128 };
char x[SIZE];
};

struct bar
{
char sameSizeAsFooX[ foo::SIZE ];
};

I agree. The problem is that editing 'foo' to introduce 'SIZE' is not
always possible. Besides, the trick with (0)-> can be used in templates
whereas you cannot expect every class that has 'x' member for which you
want to create a corresponding 'sameSizeAs..' also contains 'SIZE'...

Agreed on all counts. If it is possible, however, it is better to write
more readable (less obscure) code. As Kernighan's Law states:
"Debugging is twice as hard as writing the program, so if you write the
program as cleverly as you can, by definition, you won't be clever
enough to debug it."

Cheers! --M
 
V

Victor Bazarov

mlimber said:
[..] As Kernighan's Law states:
"Debugging is twice as hard as writing the program, so if you write
the program as cleverly as you can, by definition, you won't be clever
enough to debug it."

Kerhighan cheated here. "Hard" does not necessarily mean "impossible
to comprehend", simply "more effort required". The second part ought
to be ".. so if you strain yourself to the limit writing the program,
by definition, you won't have enough resources to debug it."

I often have to debug programs that aren't really written in a clever
way by any stretch of the meaning. But it's hard nonetheless. Which
brings out another side of the coin: sometimes if one writes a program
as cleverly as one can, it's quite hard for *somebody else* to debug.
Regardless of the relative abilities of those two people, BTW. And yet
another point in the same discussion: often the most difficult programs
to debug are the ones written applying almost no brain whatsoever.

V
 
R

red floyd

Victor said:
andrew said:
Is this code below valid C++? I'd like to use this construct but I'm
not sure if it'll be portable.

struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];

It is OK, I guess. Seems rather dangerous though, like dereferencing
a null pointer. Perhaps it would be less scary to do

char sameSizeAsFoox[ sizeof foo().x ];

(although it does require for 'foo' to be default-constructible while
your solution does not).


what's wrong with:

char sameSizeAsFoox[ sizeof (foo::x) ];


(OK, so I'm atavistic, I *like* parens around my sizeof argument).
 
K

Kai-Uwe Bux

Alf said:
* Rolf Magnus: [snip]
Actually, the exception covers not only typeid, but also sizeof:

"An expression is potentially evaluated unless either it is the operand
of the sizeof operator (5.3.3), or it is the operand of the typeid
operator and does not designate an lvalue of polymorphic class type
(5.2.8)."

No, that isn't the exception that applies to typeid.

And no, it doesn't matter whether a dereferencing is potentially
evaluated or not.

That sounds like a defect. Would you please give chapter and verse on this
one.


Best

Kai-Uwe Bux
 
G

gottlobfrege

andrew said:
Is this code below valid C++? I'd like to use this construct but I'm not
sure if it'll be portable.

struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];
};



Thanks,
Andrew

2 other things to note (besides what has been mentioned in other
replies):

1. some compilers will give a warning about the NULL dereference (ie
XCode/gcc). But to fix that you can replace 0 with 32 or whatever.
Similarly for offsetof:

#define myoffsetof(S, m) ((char *)(&(((S) *)32)->m) - 32)

why would you need to make that when there is offsetof() already?
Because the compiler (XCode/gcc again) will complain about using
offsetof for non-POD structures. (But the macro still seems to work as
expected.)

2. if 'x' was an array of doubles instead of an array of chars, you may
(depending on what you want to do with all this) also need to ensure
that your alignment requirements are correct. ie:

struct foo
{
double x[16];
};

struct bar
{
char sameSizeAsFooXButNotSameAlignment[sizeof((foo*)0->x)];
};

struct foo2
{
char achar;
foo afoo;
};

struct bar2
{
char achar;
bar abar;
}

// depending on your alignment settings, this may or may not compile:
typedef int assert[sizeof(foo2) == sizeof(bar2) ? 1 : -1];


take a look at boost type_traits and alignment_of for help with that.

Tony
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
Alf said:
* Rolf Magnus: [snip]
Actually, the exception covers not only typeid, but also sizeof:

"An expression is potentially evaluated unless either it is the operand
of the sizeof operator (5.3.3), or it is the operand of the typeid
operator and does not designate an lvalue of polymorphic class type
(5.2.8)."
No, that isn't the exception that applies to typeid.

And no, it doesn't matter whether a dereferencing is potentially
evaluated or not.

That sounds like a defect. Would you please give chapter and verse on this
one.

The typeid defect is being addressed, but the proposed resolution is to
allow null-pointer dereferencing in general; see typeid.

The nullpointer dereferencing no matter runtime evaluation or not is
because nullpointer dereferencing is stated (twice) as undefined
behavior without reference to runtime or compile time evaluation.

More about that: the para that Rolf Magnus quoted /defines/ the term
"used" as appearing in a potentially evaluated expression, and restricts
it applicability to objects and functions. The nullpointer constant is
not an object, and so it's formally never "used". It's in the same
league as division by zero: compile time or not doesn't matter.
 
S

SuperKoko

Alf said:
* Victor Bazarov:
andrew said:
Is this code below valid C++? I'd like to use this construct but I'm
not sure if it'll be portable.

struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];

It is OK, I guess. Seems rather dangerous though, like dereferencing
a null pointer. Perhaps it would be less scary to do

char sameSizeAsFoox[ sizeof foo().x ];

(although it does require for 'foo' to be default-constructible while
your solution does not).

On the one hand, dereferencing a null-pointer is formally UB no matter
which context (except in a typeid expression).
If dereferencing a null pointer means only the use of operator* to
create an lvalue, then, dereferencing a null pointer is not UB.
However, applying a lvalue-to-rvalue conversion to an lvalue or
assigning an lvalue, has UB if the lvalue refers to a an
invalid/inexisting object.
int*p=NULL;
*p=0; // UB
(*p)+0; // UB
*p; // ok
&(*p); // ok

But the standard actually states that lvalue-to-rvalue conversions in
subexpressions of sizeof expressions, are not "accessing" the data, and
I think that the intent was to say that it is not UB.

"
4.1 Lvalue-to-rvalue conversion [conv.lval]


2 The value contained in the object indicated by the lvalue is
the
rvalue result. When an lvalue-to-rvalue conversion occurs within
the
operand of sizeof (_expr.sizeof_) the value contained in the
refer-
enced object is not accessed, since that operator does not
evaluate
its operand.
"
So I think that the standard allows that for sizeof.
 
A

Alf P. Steinbach

* SuperKoko:
Alf said:
* Victor Bazarov:
andrew queisser wrote:
Is this code below valid C++? I'd like to use this construct but I'm
not sure if it'll be portable.

struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];
It is OK, I guess. Seems rather dangerous though, like dereferencing
a null pointer. Perhaps it would be less scary to do

char sameSizeAsFoox[ sizeof foo().x ];

(although it does require for 'foo' to be default-constructible while
your solution does not).

};
On the one hand, dereferencing a null-pointer is formally UB no matter
which context (except in a typeid expression).
If dereferencing a null pointer means only the use of operator* to
create an lvalue, then, dereferencing a null pointer is not UB.

Currently it is, in C++, and isn't, in C99.

It seems that C++0x will be more like C99 in this respect.

So if you'd just waited a few years before posting, you'd have been
right! ;-)
 
A

andrew queisser

andrew said:
Is this code below valid C++? I'd like to use this construct but I'm not
sure if it'll be portable.

struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];
};



Thanks,
Andrew

2 other things to note (besides what has been mentioned in other
replies):

1. some compilers will give a warning about the NULL dereference (ie
XCode/gcc). But to fix that you can replace 0 with 32 or whatever.
Similarly for offsetof:

#define myoffsetof(S, m) ((char *)(&(((S) *)32)->m) - 32)

why would you need to make that when there is offsetof() already?
Because the compiler (XCode/gcc again) will complain about using
offsetof for non-POD structures. (But the macro still seems to work as
expected.)

2. if 'x' was an array of doubles instead of an array of chars, you may
(depending on what you want to do with all this) also need to ensure
that your alignment requirements are correct. ie:

struct foo
{
double x[16];
};

struct bar
{
char sameSizeAsFooXButNotSameAlignment[sizeof((foo*)0->x)];
};

struct foo2
{
char achar;
foo afoo;
};

struct bar2
{
char achar;
bar abar;
}

// depending on your alignment settings, this may or may not compile:
typedef int assert[sizeof(foo2) == sizeof(bar2) ? 1 : -1];


take a look at boost type_traits and alignment_of for help with that.

Tony

Yeah, that's exactly the kind of stuff I'm running into - worse I'm also
throwing .NET marshalling in the mix. There are three chunks of code, one in
an embedded system (GCC), one in a C++ DLL (VC++) and one in a .NET app.

Thanks for the tip of using a non-0 constant for the fake pointer.

Andrew
 
A

andrew queisser

Thanks to everyone for the comments. I don't understand the spec issues
around whether the sizeof expression should ever be evaluated but what I've
heard is good enough for my gut to accept this trick.

I do like the foo::SIZE approach but I'm going to use this in C as well
where foo::SIZE will definitely not work.

Andrew
 
V

Victor Bazarov

andrew said:
Thanks to everyone for the comments. I don't understand the spec
issues around whether the sizeof expression should ever be evaluated
but what I've heard is good enough for my gut to accept this trick.

I do like the foo::SIZE approach but I'm going to use this in C as
well where foo::SIZE will definitely not work.

It will if you pull it out of the struct. Just define a global const
with that value and name like 'foo_x_SIZE' and use it.

V
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux:
Alf said:
* Rolf Magnus: [snip]
Actually, the exception covers not only typeid, but also sizeof:

"An expression is potentially evaluated unless either it is the operand
of the sizeof operator (5.3.3), or it is the operand of the typeid
operator and does not designate an lvalue of polymorphic class type
(5.2.8)."
No, that isn't the exception that applies to typeid.

And no, it doesn't matter whether a dereferencing is potentially
evaluated or not.

That sounds like a defect. Would you please give chapter and verse on
this one.

The typeid defect is being addressed, but the proposed resolution is to
allow null-pointer dereferencing in general; see typeid.

Do you know the number of the defect report?
The nullpointer dereferencing no matter runtime evaluation or not is
because nullpointer dereferencing is stated (twice) as undefined
behavior without reference to runtime or compile time evaluation.

Could you point me two those two clauses in the standard.
More about that: the para that Rolf Magnus quoted /defines/ the term
"used" as appearing in a potentially evaluated expression, and restricts
it applicability to objects and functions. The nullpointer constant is
not an object, and so it's formally never "used". It's in the same
league as division by zero: compile time or not doesn't matter.

Thanks for the explanation.


Best

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
Alf said:
* Kai-Uwe Bux:
Alf P. Steinbach wrote:

* Rolf Magnus:
[snip]
Actually, the exception covers not only typeid, but also sizeof:

"An expression is potentially evaluated unless either it is the operand
of the sizeof operator (5.3.3), or it is the operand of the typeid
operator and does not designate an lvalue of polymorphic class type
(5.2.8)."
No, that isn't the exception that applies to typeid.

And no, it doesn't matter whether a dereferencing is potentially
evaluated or not.
That sounds like a defect. Would you please give chapter and verse on
this one.
The typeid defect is being addressed, but the proposed resolution is to
allow null-pointer dereferencing in general; see typeid.

Do you know the number of the defect report?

No, sorry, I don't recall, execpt that it isn't about typeid per se;
it's about nullpointer dereferencing and one-past-array access.

Hey, wait a minute, I had some correspondence about that.

[searching inbox...]

OK, "CWG 232", that's from correspondence related to a comp.std.c++
thread titled "Are references to not-quite-objects legal?", late 2005.

Probably that's the one; it's at <url:
http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#232>.

It's a bit funny because it starts out noting the current wording of the
standard (UB), then goes on to discuss original intent, and concludes
that all's OK because of the intent -- which it definitely isn't,
since otherwise that DR wouldn't have existed.

Could you point me to those two clauses in the standard.

The first one is the definition of "undefined behavior". :)
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

red said:
struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];

It is OK, I guess. Seems rather dangerous though, like dereferencing
a null pointer. Perhaps it would be less scary to do

char sameSizeAsFoox[ sizeof foo().x ];

(although it does require for 'foo' to be default-constructible while
your solution does not).


what's wrong with:

char sameSizeAsFoox[ sizeof (foo::x) ];

A simple solution:

struct foo {
typedef char TypeOfX [128];
TypeOfX x;
};

struct bar {
char sameSizeAsFooX[ sizeof (foo::TypeOfX) ];
};
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux:
Alf said:
* Kai-Uwe Bux:
Alf P. Steinbach wrote:

* Rolf Magnus:
[snip]
Actually, the exception covers not only typeid, but also sizeof:

"An expression is potentially evaluated unless either it is the
operand of the sizeof operator (5.3.3), or it is the operand of the
typeid operator and does not designate an lvalue of polymorphic class
type (5.2.8)."
No, that isn't the exception that applies to typeid.

And no, it doesn't matter whether a dereferencing is potentially
evaluated or not.
That sounds like a defect. Would you please give chapter and verse on
this one.
The typeid defect is being addressed, but the proposed resolution is to
allow null-pointer dereferencing in general; see typeid.

Do you know the number of the defect report?

No, sorry, I don't recall, execpt that it isn't about typeid per se;
it's about nullpointer dereferencing and one-past-array access.

Hey, wait a minute, I had some correspondence about that.

[searching inbox...]

OK, "CWG 232", that's from correspondence related to a comp.std.c++
thread titled "Are references to not-quite-objects legal?", late 2005.

Probably that's the one; it's at <url:
http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#232>.

It's a bit funny because it starts out noting the current wording of the
standard (UB), then goes on to discuss original intent, and concludes
that all's OK because of the intent -- which it definitely isn't,
since otherwise that DR wouldn't have existed.

Could you point me to those two clauses in the standard.

The first one is the definition of "undefined behavior". :)

Should you be referring to 1.3.12, I would like to ask how you arrive at the
conclusion that sizeof( ((foo*)0)->x ) is undefined from the wording of
1.3.12. Your original claim was:

On the one hand, dereferencing a null-pointer is formally UB no matter
which context (except in a typeid expression).

I have tried to derive this claim for the wording in the standard, but I
failed. Could you please provide a reasoning that is a little more detailed
an open to verification.


Best

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
Alf said:
* Kai-Uwe Bux:
Alf P. Steinbach wrote:

* Kai-Uwe Bux:
Alf P. Steinbach wrote:

* Rolf Magnus:
[snip]
Actually, the exception covers not only typeid, but also sizeof:

"An expression is potentially evaluated unless either it is the
operand of the sizeof operator (5.3.3), or it is the operand of the
typeid operator and does not designate an lvalue of polymorphic class
type (5.2.8)."
No, that isn't the exception that applies to typeid.

And no, it doesn't matter whether a dereferencing is potentially
evaluated or not.
That sounds like a defect. Would you please give chapter and verse on
this one.
The typeid defect is being addressed, but the proposed resolution is to
allow null-pointer dereferencing in general; see typeid.
Do you know the number of the defect report?
No, sorry, I don't recall, execpt that it isn't about typeid per se;
it's about nullpointer dereferencing and one-past-array access.

Hey, wait a minute, I had some correspondence about that.

[searching inbox...]

OK, "CWG 232", that's from correspondence related to a comp.std.c++
thread titled "Are references to not-quite-objects legal?", late 2005.

Probably that's the one; it's at <url:
http://std.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#232>.

It's a bit funny because it starts out noting the current wording of the
standard (UB), then goes on to discuss original intent, and concludes
that all's OK because of the intent -- which it definitely isn't,
since otherwise that DR wouldn't have existed.

The nullpointer dereferencing no matter runtime evaluation or not is
because nullpointer dereferencing is stated (twice) as undefined
behavior without reference to runtime or compile time evaluation.
Could you point me to those two clauses in the standard.
The first one is the definition of "undefined behavior". :)

Should you be referring to 1.3.12, I would like to ask how you arrive at the
conclusion that sizeof( ((foo*)0)->x ) is undefined from the wording of
1.3.12. Your original claim was:

On the one hand, dereferencing a null-pointer is formally UB no matter
which context (except in a typeid expression).

I have tried to derive this claim for the wording in the standard, but I
failed. Could you please provide a reasoning that is a little more detailed
an open to verification.

Sorry about the vague description, I thought you'd find it readily
enough, especially since mentioned in the DR I linked to: it's §1.9/4.
 
J

Jack Klein

* Victor Bazarov:
andrew said:
Is this code below valid C++? I'd like to use this construct but I'm
not sure if it'll be portable.

struct foo
{
char x[128];
};
struct bar
{
char sameSizeAsFooX[ sizeof ((foo *)0)->x ];

It is OK, I guess. Seems rather dangerous though, like dereferencing
a null pointer. Perhaps it would be less scary to do

char sameSizeAsFoox[ sizeof foo().x ];

(although it does require for 'foo' to be default-constructible while
your solution does not).

On the one hand, dereferencing a null-pointer is formally UB no matter
which context (except in a typeid expression).

Yes, dereferencing a null pointer is undefined behavior. However
since the C++ standard (like the C standard) states:

"The sizeof operator yields the number of bytes in the object
representation of its operand. The operand is either an expression,
which is not evaluated, or a parenthesized typeid."

Since the expression is itself is not evaluated, the pointer is never
dereferenced, and the behavior is completely defined.
On the other hand, the only harm it can do inside a sizeof is to make
som rather dumb compiler choke on the expression, and it's an old idiom.

On the third & gripping hand, at the technical level, where we don't
care about how a better design might make the need go away, this is
really a job for the Unimplemented Fake Function, the UFF,

foo& foon();

char samesizeAsFoox[ sizeof( foon().x ) ];
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top