double casts

M

Martijn

Hi,

Those familiar with Windows programming may be familiar with the windowsx.h
header. In this header macros exist that use double casts, like so (hope
this is readable):

#define ComboBox_LimitText(hwndCtl,cchLimit) \

((int)(DWORD)SendMessage((hwndCtl),CB_LIMITTEXT,(WPARAM)(int)(cchLimit),0))

Why is this? Does this help in typechecking? (I would think not).

Thanks for the info,
 
J

Jack Klein

Hi,

Those familiar with Windows programming may be familiar with the windowsx.h
header. In this header macros exist that use double casts, like so (hope
this is readable):

Your text is readable, the concept of what the code is doing is not.
#define ComboBox_LimitText(hwndCtl,cchLimit) \

((int)(DWORD)SendMessage((hwndCtl),CB_LIMITTEXT,(WPARAM)(int)(cchLimit),0))

Why is this? Does this help in typechecking? (I would think not).

Casts never help in type checking, they specifically exist to defeat
type checking.
Thanks for the info,

For a variety of reasons, Windows header files are a complete and
total mess. If you really want to delve into the details, I'd suggest
a Windows programming group or one of Microsoft's support groups.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
 
X

xarax

Jack Klein said:
Your text is readable, the concept of what the code is doing is not.


Casts never help in type checking, they specifically exist to defeat
type checking.


For a variety of reasons, Windows header files are a complete and
total mess. If you really want to delve into the details, I'd suggest
a Windows programming group or one of Microsoft's support groups.

That wasn't his question.

His question is this: When is it necessary, if ever, to use
a double cast?

For example:

1. (WPARM)(int)(cchLimit)

2. (WPARM)(cchLimit)

Are these two expressions *always* equivalent? Is there ever
a situation where the intermediate cast (int) is necessary
to get a "correct" final cast to (WPARM)?

Just ignore whatever "WPARM" is supposed to be and think
about the generic question of double casting versus single casting.
Don't get distracted about Windows. This is really a C question.

For you C gurus out there, what is the definitive answer?
 
J

Joona I Palaste

That wasn't his question.
His question is this: When is it necessary, if ever, to use
a double cast?
For example:
1. (WPARM)(int)(cchLimit)
2. (WPARM)(cchLimit)
Are these two expressions *always* equivalent? Is there ever
a situation where the intermediate cast (int) is necessary
to get a "correct" final cast to (WPARM)?
Just ignore whatever "WPARM" is supposed to be and think
about the generic question of double casting versus single casting.
Don't get distracted about Windows. This is really a C question.
For you C gurus out there, what is the definitive answer?

I am not a C guru and cannot answer the OP's question, but I can say
for certain that in general, an expression of the type (A)(B)something
is not the same thing as (A)something.
Proof:
int i;
int *p1 = (int *)&i;
int *p2 = (int *)(long)&i;
p1 is guaranteed to store i's address. p2 is not.
 
K

Keith Thompson

Joona I Palaste said:
I am not a C guru and cannot answer the OP's question, but I can say
for certain that in general, an expression of the type (A)(B)something
is not the same thing as (A)something.
Proof:
int i;
int *p1 = (int *)&i;
int *p2 = (int *)(long)&i;
p1 is guaranteed to store i's address. p2 is not.

Ok, that's a good example of when a double cast is harmful. Are there
any cases where it's helpful -- i.e., where (A)(B)something differs
from (A)something *and* (A)(B)something does something useful that
(A)something does not. In other words, if I see (A)(B)something in
source code, can I always delete the "(B)" without breaking anything?

Here's one example (assume 8-bit char):

unsigned int n = 257;
float f0 = (float)n; /* f0 == 257.0 */
float f1 = (float)(unsigned char)n; /* f1 == 1.0 */

Are there any examples not involving deliberate loss of information?
 
C

CBFalconer

xarax said:
That wasn't his question.

His question is this: When is it necessary, if ever, to use
a double cast?

For example:

1. (WPARM)(int)(cchLimit)
2. (WPARM)(cchLimit)

Are these two expressions *always* equivalent? Is there ever
a situation where the intermediate cast (int) is necessary
to get a "correct" final cast to (WPARM)?

Just ignore whatever "WPARM" is supposed to be and think
about the generic question of double casting versus single
casting. Don't get distracted about Windows. This is really
a C question.

For you C gurus out there, what is the definitive answer?

One possibility that occurs to me is when you don't know the
signedness of the original item. Thus you might need:

(size_t) ((unsigned char) ch)

which could avoid generating a large number from '\0xff', say.

However the essential illegitimacy of windows headers remains :)
 
S

Sheldon Simms

Ok, that's a good example of when a double cast is harmful. Are there
any cases where it's helpful -- i.e., where (A)(B)something differs
from (A)something *and* (A)(B)something does something useful that
(A)something does not. In other words, if I see (A)(B)something in
source code, can I always delete the "(B)" without breaking anything?

Here's one example (assume 8-bit char):

unsigned int n = 257;
float f0 = (float)n; /* f0 == 257.0 */
float f1 = (float)(unsigned char)n; /* f1 == 1.0 */

Are there any examples not involving deliberate loss of information?

I have some code that uses a few double casts, but they rely on
implementation-defined behavior -- specifically that an unsigned
integer cast to a signed integer becomes the signed integer value
with the same bit value as the unsigned integer.

i.e.:
unsigned char uc = 255;
signed char sc = (signed char)uc;
/* sc has value -1 */

The code does things like this:

short s;
unsigned char uca, ucb;
...
++uca; /* increment with intended wrap from 255 to 0 */
...
--ucb; /* decrement with intended wrap from 0 to 255 */
...
s = (short)(signed char)uca + (short)(signed char)ucb;

The (signed char) casts are necessary to force the two
values being added into the range -128..+127 and the (short)
casts are necessary to prevent problems when the sum would
overflow a signed char.
 
P

Peter Nilsson

Sheldon Simms said:
unsigned char uc = 255;
signed char sc = (signed char)uc;
/* sc has value -1 */

There is no standard guarantee that sc has the value -1.

If 255 <= SCHAR_MAX, then sc gets the value 255, otherwise 255 is
converted in an implementation defined way. [C99 allows an
implementation defined signal to be raised, although that's highly
unlikely.]
 
S

Sheldon Simms

Sheldon Simms said:
unsigned char uc = 255;
signed char sc = (signed char)uc;
/* sc has value -1 */

There is no standard guarantee that sc has the value -1.

If 255 <= SCHAR_MAX, then sc gets the value 255, otherwise 255 is
converted in an implementation defined way. [C99 allows an
implementation defined signal to be raised, although that's highly
unlikely.]

Thanks for pointing that out, even though I had already said that
my code relied on implementation defined behavior. Since you didn't
bother quoting it, here's what I said:

-Sheldon
 
P

Peter Nilsson

Sheldon Simms said:
...
unsigned char uc = 255;
signed char sc = (signed char)uc;
/* sc has value -1 */

There is no standard guarantee that sc has the value -1.

If 255 <= SCHAR_MAX, then sc gets the value 255, otherwise 255 is
converted in an implementation defined way. [C99 allows an
implementation defined signal to be raised, although that's highly
unlikely.]

Thanks for pointing that out, even though I had already said that
my code relied on implementation defined behavior.

Since you didn't bother quoting it, here's what I said:

It's not that I didn't bother; I wanted to isolate the specific code that my
comment referred to.

Continued by...

Which isn't actually enough to give sc the value -1, even on an 8 bit char
implementation. You need the further assumption of two's complement.

Even implementation specific code is worth analysing from the point of view
of trying to make it (and similar code) non implementation dependant. To do
that, we (or at least I) need to discus what those dependancies are, i.e.
what the standards guarantee, and what they don't. For me, that is the whole
point of comp.lang.c.

In any case, I apologise if you feel I misrepresented you by taking your
post too far out of context.
 
S

Sheldon Simms

Sheldon Simms said:
Sheldon Simms <[email protected]> wrote in message
...
unsigned char uc = 255;
signed char sc = (signed char)uc;
/* sc has value -1 */

There is no standard guarantee that sc has the value -1.

If 255 <= SCHAR_MAX, then sc gets the value 255, otherwise 255 is
converted in an implementation defined way. [C99 allows an
implementation defined signal to be raised, although that's highly
unlikely.]

Thanks for pointing that out, even though I had already said that
my code relied on implementation defined behavior.

Since you didn't bother quoting it, here's what I said:

It's not that I didn't bother; I wanted to isolate the specific code that my
comment referred to.

Continued by...

Which isn't actually enough to give sc the value -1, even on an 8 bit char
implementation. You need the further assumption of two's complement.

True. I realized after posting that this sentence didn't completely
describe the kind of implementation-defined behavior I was relying on.
 
D

Dan Pop

In said:
Ok, that's a good example of when a double cast is harmful. Are there
any cases where it's helpful -- i.e., where (A)(B)something differs
from (A)something *and* (A)(B)something does something useful that
(A)something does not. In other words, if I see (A)(B)something in
source code, can I always delete the "(B)" without breaking anything?

Here's one example (assume 8-bit char):

unsigned int n = 257;
float f0 = (float)n; /* f0 == 257.0 */
float f1 = (float)(unsigned char)n; /* f1 == 1.0 */

Are there any examples not involving deliberate loss of information?

Sure:

int n = -1;
double f0 = (double)n; /* f0 == -1.0 */
double f1 = (double)(unsigned)n; /* f1 == (double)UINT_MAX */

No information is lost. The value of n can be retrieved from the value
of f1. That is, assuming that a double's mantissa has more bits than an
unsigned.

Dan
 
M

Martijn

#define ComboBox_LimitText(hwndCtl,cchLimit) \
((int)(DWORD)SendMessage((hwndCtl),CB_LIMITTEXT,(WPARAM)(int)(cchLimit),0))

Why is this? Does this help in typechecking? (I would think not).

Thanks for the info,

Thanks for everybody who gave this a thought. I guess I'll post it on one
of the MS group, see if any ex-microsoftie has anything to say about it :)
 
M

Mantorok Redgormor

Joona I Palaste said:
I am not a C guru and cannot answer the OP's question, but I can say
for certain that in general, an expression of the type (A)(B)something
is not the same thing as (A)something.
Proof:
int i;
int *p1 = (int *)&i;
int *p2 = (int *)(long)&i;
p1 is guaranteed to store i's address. p2 is not.

why would the long mess this up? if i's address can be represented as
long, then wouldn't that be perfectly okay?

- nethlek
 
J

Joona I Palaste

why would the long mess this up? if i's address can be represented as
long, then wouldn't that be perfectly okay?

IF i's address can be represented as long. That's a mighty big IF you
got there.
And even if that's true, there's no guarantee the host CPU returns
scalars and pointers in the same way. It could use different registers,
for instance, in which case p2 will be pure garbage.
 
D

Dan Pop

In said:
IF i's address can be represented as long. That's a mighty big IF you
got there.

And even then, there is no guarantee that the conversion between pointers
and integers (and vice versa) yields meaningful results. Only C99
provides such a guarantee, *if* intptr_t and uintptr_t are defined (and
used in the conversion) and if the pointers are void pointers. In such
a case, the code would be:

int *p2 = (void *)(intptr_t)(void *)&i;

which is kinda silly, of course. A final cast to int * is not necessary.
And even if that's true, there's no guarantee the host CPU returns
scalars and pointers in the same way. It could use different registers,
for instance, in which case p2 will be pure garbage.

Huh? I can see no function call in the code. What am I missing?

Dan
 
J

Joona I Palaste

Huh? I can see no function call in the code. What am I missing?

You're missing the fact that I can sometimes fumble up in my
terminology. By "return" I meant something like "store". Is a cast to
an (int *) allowed to read from a different register than a cast to a
(long)?
 
D

Dan Pop

In said:
You're missing the fact that I can sometimes fumble up in my
terminology. By "return" I meant something like "store". Is a cast to
an (int *) allowed to read from a different register than a cast to a
(long)?

Now, you're severely confused: the evaluation of an expression is defined
in terms of values, it is immaterial where these values are stored, as
long as all of them are obtained via legit means. It is compiler's job
to do the right thing and it has all the necessary information for that.

Such things can happen only when misdeclared functions are involved.
Consider the following *complete* program (on a C89 implementation):

int main()
{
return sin(0.0);
}

In the absence of an explicit declaration, sin() is implicitly declared
as returning int. Therefore, after generating the function call, the
compiler will expect the return value to be in whatever place a function
returning int will put its return value. But the sin() function doesn't
"know" that and it will put its return value in whatever place a function
returning double is supposed to put its return value. The final result
being that return will use an indeterminate value (but undefined behaviour
has already been invoked by the time sin() was called.

Even worse things can happen if the stack is used for passing back the
return value, because the caller generates a certain stack layout, while
the callee expects another, possibly corrupting stack data containing the
callers local variables or its return address.

Dan
 
J

Joona I Palaste

Now, you're severely confused: the evaluation of an expression is defined
in terms of values, it is immaterial where these values are stored, as
long as all of them are obtained via legit means. It is compiler's job
to do the right thing and it has all the necessary information for that.
Such things can happen only when misdeclared functions are involved.
Consider the following *complete* program (on a C89 implementation):
int main()
{
return sin(0.0);
}
In the absence of an explicit declaration, sin() is implicitly declared
as returning int. Therefore, after generating the function call, the
compiler will expect the return value to be in whatever place a function
returning int will put its return value. But the sin() function doesn't
"know" that and it will put its return value in whatever place a function
returning double is supposed to put its return value. The final result
being that return will use an indeterminate value (but undefined behaviour
has already been invoked by the time sin() was called.

Thanks, Dan! You deconfused me pretty well. So in other words, when
evaluating the expressions, the compiler "knows" the correct types and
the register mix-up I was talking about can't happen. But when the
compiler is evaluating function calls, it doesn't necessarily "know" how
the return values of the functions will be used.

--
/-- Joona Palaste ([email protected]) ------------- Finland --------\
\-- http://www.helsinki.fi/~palaste --------------------- rules! --------/
"You can pick your friends, you can pick your nose, but you can't pick your
relatives."
- MAD Magazine
 
A

Arthur J. O'Dwyer

You're missing the fact that I can sometimes fumble up in my
terminology. By "return" I meant something like "store". Is a cast to
an (int *) allowed to read from a different register than a cast to a
(long)?

(ITYM: Is an implementation allowed to make conversions between
[non-intptr_t] integral and pointer types do funky, unpredictable
stuff?)

Technically, yes, as long as the implementation documents that
behavior (N869 6.3.2.3#5). Of course, that would be a pretty silly
implementation; it'd be much more user-friendly to simply convert all
pointers to zero upon conversion to 'long'.

And no, no *sensible* compiler would ever do such a thing. I
don't think it would simplify anything if one made the compiler
do that.

-Arthur
 

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

Similar Threads


Members online

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top