Better casts?

Discussion in 'C Programming' started by Ralf, Jan 21, 2006.

  1. Ralf

    Ralf Guest

    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?
     
    Ralf, Jan 21, 2006
    #1
    1. Advertising

  2. Ralf wrote:
    > 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>

    --
    Nick Keighley
     
    Nick Keighley, Jan 21, 2006
    #2
    1. Advertising

  3. Ralf wrote:

    > 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
     
    Vladimir S. Oka, Jan 21, 2006
    #3
  4. Nick Keighley wrote:

    > Ralf wrote:
    >> 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>
    >


    A better programmer would be preferable... ;-)

    Cheers

    Vladimir
     
    Vladimir S. Oka, Jan 21, 2006
    #4
  5. Ralf

    Flash Gordon Guest

    Vladimir S. Oka wrote:
    > Ralf wrote:
    >
    >> 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.


    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

    >> 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.


    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.

    >> 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.


    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.

    >> 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.


    A lot of the time such casts are not required.

    >> 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...


    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.

    >> 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...


    Added to which I would not add casts to code to shut up compiler warnings.
    --
    Flash Gordon
    Living in interesting times.
    Although my email address says spam, it is real and I read it.
     
    Flash Gordon, Jan 21, 2006
    #5
  6. "Ralf" <> writes:
    > 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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jan 21, 2006
    #6
  7. In article <-gordon.me.uk>,
    Flash Gordon <> wrote:

    > 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?
     
    Christian Bau, Jan 22, 2006
    #7
  8. Flash Gordon wrote:

    > Vladimir S. Oka wrote:
    >> Ralf wrote:
    >>
    >>> 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.

    >
    > 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 <> wrote:
    > 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.

    >>> 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.

    >
    > 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
     
    Vladimir S. Oka, Jan 22, 2006
    #8
  9. Christian Bau <> writes:
    > In article <-gordon.me.uk>,
    > Flash Gordon <> wrote:
    >
    >> 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.

    [...]

    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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Jan 22, 2006
    #9
  10. Ralf

    Thad Smith Guest

    Keith Thompson wrote:

    > 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.

    --
    Thad
     
    Thad Smith, Jan 23, 2006
    #10
  11. Ralf

    Al Balmer Guest

    On 21 Jan 2006 06:25:42 -0800, "Ralf" <> wrote:

    >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.

    --
    Al Balmer
    Sun City, AZ
     
    Al Balmer, Jan 23, 2006
    #11
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?Q2hyaXMgRGF2b2xp?=

    Web casts in ASP.Net

    =?Utf-8?B?Q2hyaXMgRGF2b2xp?=, Oct 19, 2005, in forum: ASP .Net
    Replies:
    1
    Views:
    512
    clintonG
    Oct 19, 2005
  2. cgbusch
    Replies:
    2
    Views:
    331
    Sudsy
    Jul 8, 2003
  3. Peter Bencsik
    Replies:
    2
    Views:
    839
  4. Andrew Thompson
    Replies:
    8
    Views:
    154
    Premshree Pillai
    Jun 7, 2005
  5. Replies:
    2
    Views:
    56
    Mark H Harris
    May 13, 2014
Loading...

Share This Page