Better casts?

R

Ralf

Regarding numerical types, in my view, casts fall in one of two
categories:
1. Casts that change the value of an object
2. Casts that are actually redundant, but get rid of compiler/lint
warnings

As an example, consider this code:

unsigned int ui;
...
unsigned char uc = (unsigned char)ui;

Here, it is not clear from the code what the developer wanted to
achieve:
1. It is possible that ui exceeds the unsigned char range and the
programmer only wants to look at the lower (e. g. 8) bits. Basically,
he wants to cut off significant bits and hence wants to change the
original value.
2. The developer knows that ui cannot hold values that exceed the
unsigned char range, so assigning ui to uc is safe and doesn't lose
bits (i. e. the original value is preserved). He only casts to shut up
the compiler/Lint.

Would it make sense to introduce cast macros that clearly indicate what
the programmer wants to do, as in:

#define VALUE_CAST(type, e) ( (type)(e) )
#define WARNING_CAST(type, e) ( (type)(e) )

In the code below the purpose of the cast would be self-explanatory:

unsigned char uc = WARNING_CAST(unsigned char, ui);

Maybe WARNING_CAST could be even augmented by an assert checking if the
source object is in the range of the target type.
Any comments?
 
N

Nick Keighley

Ralf said:
Regarding numerical types, in my view, casts fall in one of two
categories:
1. Casts that change the value of an object
2. Casts that are actually redundant, but get rid of compiler/lint
warnings

couldn't you just get a better compiler?

<snip>
 
V

Vladimir S. Oka

Ralf said:
Regarding numerical types, in my view, casts fall in one of
two categories:
1. Casts that change the value of an object

These, I'd call bugs... Cast are decidedly not the language
constructs one should use to change the value of an object.
2. Casts that are actually redundant, but get rid of
compiler/lint

These may not be redundant at all, e.g. where there's genuine
need to change the type of the object containing the value in
question.
warnings

As an example, consider this code:

unsigned int ui;
...
unsigned char uc = (unsigned char)ui;

Here, it is not clear from the code what the developer wanted
to achieve:
1. It is possible that ui exceeds the unsigned char range
and the
programmer only wants to look at the lower (e. g. 8) bits.
Basically, he wants to cut off significant bits and hence
wants to change the original value.

No, the programmer does not really /want/ to change the value,
rather she's just hoping that the high order bits will all be
zero. It is entirely possible to check for that before casting.
2. The developer knows that ui cannot hold values that
exceed the
unsigned char range, so assigning ui to uc is safe and doesn't
lose bits (i. e. the original value is preserved). He only
casts to shut up the compiler/Lint.

He may actually cast because the design requires that the value
from this point on resides in an object of a different type.
Think hardware registers in an embedded design here.
Would it make sense to introduce cast macros that clearly
indicate what the programmer wants to do, as in:

#define VALUE_CAST(type, e) ( (type)(e) )
#define WARNING_CAST(type, e) ( (type)(e) )

In the code below the purpose of the cast would be
self-explanatory:

unsigned char uc = WARNING_CAST(unsigned char, ui);

These are certainly possible, but I don't think they bring much
to the party...
Maybe WARNING_CAST could be even augmented by an assert
checking if the source object is in the range of the target
type. Any comments?

And adding asserts to these would certainly be death by
defensive programming. ;-)

My tuppence, anyway...

Cheers

Vladimir
 
F

Flash Gordon

Vladimir said:
These, I'd call bugs... Cast are decidedly not the language
constructs one should use to change the value of an object.

Not always the case, for example it can be the right thing to do with
the to* functions. Have a look at this thread for a discussion of the
issue
http://groups.google.co.uk/group/co...oup:comp.lang.c&rnum=1&hl=en#f2813857161a2020
These may not be redundant at all, e.g. where there's genuine
need to change the type of the object containing the value in
question.

True. For the OP, examples include printing a pointer using the %p
format specifier, where you need to convert the pointer to a void*
before passing it to printf.
No, the programmer does not really /want/ to change the value,
rather she's just hoping that the high order bits will all be
zero. It is entirely possible to check for that before casting.

How do you know what the programmer wants without asking the programmer?
There are legal well defined ways to use casts to change values,
although whether they are the method you or I would choose is another
matter.
He may actually cast because the design requires that the value
from this point on resides in an object of a different type.
Think hardware registers in an embedded design here.

A lot of the time such casts are not required.
These are certainly possible, but I don't think they bring much
to the party...

Agreed. I definitely would not use such macros nor would I want them in
code I had to deal with. If it's not obvious why the cast is there (or
in any other instance where the purpose of the code is not clear) you
should use comments.
And adding asserts to these would certainly be death by
defensive programming. ;-)

My tuppence, anyway...

Added to which I would not add casts to code to shut up compiler warnings.
 
K

Keith Thompson

Ralf said:
Regarding numerical types, in my view, casts fall in one of two
categories:
1. Casts that change the value of an object
2. Casts that are actually redundant, but get rid of compiler/lint
warnings

As an example, consider this code:

unsigned int ui;
...
unsigned char uc = (unsigned char)ui;

This is precisely equivalent to

unsigned int ui;
...
unsigned char uc = ui;

You could argue that the version with the cast is more explicit. On
the other hand, it has to repeat the target type, and it introduces
possibilities for error. For example, suppose you change the type of
the variable to unsigned short, but forget to change the cast.

*All* casts should be viewed with suspicion. There are a few cases
where they're necessary:

Passing an argument to a function that takes a variable number and
type of parameters. (You should always have a visible prototype
for any function you call; for ordinary functions, this will
impose an implicit conversion.)

Arithmetic conversions within expressions. For example:
int x = ...;
int y = ...;
long product = (long)x * (long)y;
Without the casts, the multiplication will be done in type int,
and the value converted to long afterwards. (Actually only one of
the casts is necessary, but I like the symmetry.) (And yes, I'm
assuming that long is wider than int.)

Pointer conversions in deliberately non-portable code.

Probably other cases I haven't thought of.
 
C

Christian Bau

Flash Gordon said:
How do you know what the programmer wants without asking the programmer?
There are legal well defined ways to use casts to change values,
although whether they are the method you or I would choose is another
matter.

Example: I have a floating point value x which is known to be in the
range 0 <= x < 256. I want an unsigned char c with the value floor (x).

c = (unsigned char) x;

is a completely legal way to do this.

c = x;

is also a completely legal way to do this; however, it looks
suspiciously like a programming error.

/* This assignment is ok, we know that 0 <= x < 256 */
c = x;

is also completely legal, and it looks ok to me and you, but it might
not look ok to my compiler or your compiler.

c = floor (x);

is ok, but inefficient, it still looks like a programming error because
x might be outside the range 0 to 255.

c = (unsigned char) floor (x);

is ok, but inefficient, and what is the point of using floor () if you
cast the result to unsigned char immediately afterwards?
 
V

Vladimir S. Oka

Flash said:
Not always the case, for example it can be the right thing to
do with the to* functions. Have a look at this thread for a
discussion of the issue
http://groups.google.co.uk/group/co...oup:comp.lang.c&rnum=1&hl=en#f2813857161a2020

In that thread Richard said:

Richard Heathfield said:
Yes, it takes an int. But if you are going to cast it to
anything, cast it to unsigned char.
Why? Because if you give it anything that is neither EOF nor
representable as an unsigned char, you invoke undefined
behaviour. If you plan on passing
EOF to toupper(), reconsider your design. Otherwise, cast to
unsigned char if there is the slightest doubt that your data
may not be representable as unsigned char. Rely on good
testing procedures to pick up data errors. Or
cut code to improve your validation.

I may not have made it clear in my post, by I was actually
trying to make his last point. Yes, you can use casts that
change the value to prevent UB or other problems, but
preferable route is to check your data explicitely.
How do you know what the programmer wants without asking the
programmer? There are legal well defined ways to use casts to
change values, although whether they are the method you or I
would choose is another matter.

You're right, we can't tell, and that's where I think the
problem lies. I'd certainly strongly discourage using casts to
change the value of an object: test, change value without
casting if needed, assign to a different type of object (with
or without explicit cast, depending on the case) would be my
preferable way.

I agree with the rest of what you said, hence the big snip
here...

Cheers

Vladimir
 
K

Keith Thompson

Christian Bau said:
Example: I have a floating point value x which is known to be in the
range 0 <= x < 256. I want an unsigned char c with the value floor (x).

c = (unsigned char) x;

is a completely legal way to do this.

c = x;

is also a completely legal way to do this; however, it looks
suspiciously like a programming error.

/* This assignment is ok, we know that 0 <= x < 256 */
c = x;

is also completely legal, and it looks ok to me and you, but it might
not look ok to my compiler or your compiler.
[...]

A compiler might give you a warning about this, since it could invoke
undefined behavior of x is outside the range of unsigned char. But a
compiler could as easily give you a warning about the cast -- or about
that ugly tie you're wearing. Once it issues the warning, it *must*
compile the code correctly.

If you're adding a cast for the sole purpose of inhibiting a compiler
warning, you're probably doing something wrong. The classic example
is casting the result of malloc() rather than adding a
"#include <stdlib.h>". This isn't that bad, of course, but it is
potentially dangerous. Consider what happens if you change the type
of c (to signed char, or unsigned short, or whatever) and forget to
change the cast.
 
T

Thad Smith

Keith said:
If you're adding a cast for the sole purpose of inhibiting a compiler
warning, you're probably doing something wrong.

That depends, of course, on the warning. I grant that casts can be
abused, but I see no problem with adding a cast to suppress a warning
that a value may lose precision on assigning an int to a unsigned char,
for example, when I know that the value is within range of the
assignment variable. That was the point of the warning cast mentioned
by the OP, I believe.

I prefer to suppress a warning that I investigated so that subsequent
compiler summaries aren't cluttered with warnings I have already checked.
 
A

Al Balmer

Regarding numerical types, in my view, casts fall in one of two
categories:
1. Casts that change the value of an object
2. Casts that are actually redundant, but get rid of compiler/lint
warnings

3. Casts which hide a programmer error.
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top