ObjC BOOL and boolean c expressions

  • Hi,

    I'm not sure where and when but I recall reading something about ObjC
    BOOL type values YES and NO not being the same as whet a boolean
    expression might produce.
    I can't recall the exact details but there was some kind of gotcha
    related to this. For example, the expression

    BOOL myBool=(myInt==1);

    might set myBool differently then

    BOOL myBool=(myInt==1)?YES:NO;

    Or maybe it was that

    if (myBool)
    [foo bar];

    will perform differently then
    if (myBool==YES)
    [foo bar];

    Or maybe it was just that if I do this

    int myInt=4096;
    BOOL myBool=myInt;

    Then if (myBool) will evaluate to false.

    This issue was bugging me for quite some time and I'm wondering if
    somebody recalls this discussion.

    TIA

    Eyal Redler
    ======================================
    RedleX - Makers of Mellel
    www.mellel.com
    <eyal...>
    <eyredler...>
  • --- Eyal Redler <eyredler...> wrote:

    > Hi,
    >
    > I'm not sure where and when but I recall reading
    > something about ObjC
    > BOOL type values YES and NO not being the same as
    > whet a boolean
    > expression might produce.
    > I can't recall the exact details but there was some
    > kind of gotcha
    > related to this. For example, the expression
    >
    > BOOL myBool=(myInt==1);
    >
    > might set myBool differently then
    >
    > BOOL myBool=(myInt==1)?YES:NO;

    The two expressions are equivalent. YES is defined as
    (BOOL)1, while NO is defined as (BOOL)0.

    >
    > Or maybe it was that
    >
    > if (myBool)
    > [foo bar];
    >
    > will perform differently then
    > if (myBool==YES)
    > [foo bar];

    I wouldn't say it will work differently, but it might.
    There's no language constraint that says a BOOL can
    only be set to YES or NO (though I wouldn't
    intentionally set a BOOL to some other value). So if a
    method returns a nonzero value that is not 1, if
    (myBool) will evaluate to true while if (myBool ==
    YES) will evaluate to false.

    Cheers,
    Chuck


    ____________________________________________________________________________________
    Got a little couch potato?
    Check out fun summer activities for kids.
    http://search.yahoo.com/search?fr=oni_on_mail&p=summer+activities+for+k
    ids&cs=bz
  • I *think* that the C spec doesn't specify the result of a boolean
    expression other than "zero means false, nonzero means true." So from
    a "compliance with the spec" standpoint, the first version might
    return different results from the second version.

    In practice, I've never seen a compiler which didn't generate 1 for a
    true result when computing a boolean expression. And YES == 1.

    I am sure that an exception exists out there somewhere, on some weird
    hardware/compiler combination, where a true boolean expression
    returns a value other than one, but I doubt you will see this
    becoming an issue in your code if you keep using standard tools.

    On Aug 30, 2007, at 11:19 PM, Charles Steinman wrote:

    >
    > --- Eyal Redler <eyredler...> wrote:
    >
    >> Hi,
    >>
    >> I'm not sure where and when but I recall reading
    >> something about ObjC
    >> BOOL type values YES and NO not being the same as
    >> whet a boolean
    >> expression might produce.
    >> I can't recall the exact details but there was some
    >> kind of gotcha
    >> related to this. For example, the expression
    >>
    >> BOOL myBool=(myInt==1);
    >>
    >> might set myBool differently then
    >>
    >> BOOL myBool=(myInt==1)?YES:NO;
    >
    > The two expressions are equivalent. YES is defined as
    > (BOOL)1, while NO is defined as (BOOL)0.
    >
    >>
    >> Or maybe it was that
    >>
    >> if (myBool)
    >> [foo bar];
    >>
    >> will perform differently then
    >> if (myBool==YES)
    >> [foo bar];
    >
    > I wouldn't say it will work differently, but it might.
    > There's no language constraint that says a BOOL can
    > only be set to YES or NO (though I wouldn't
    > intentionally set a BOOL to some other value). So if a
    > method returns a nonzero value that is not 1, if
    > (myBool) will evaluate to true while if (myBool ==
    > YES) will evaluate to false.
    >
    > Cheers,
    > Chuck
  • On 8/31/07, John Stiles <JStiles...> wrote:
    > I *think* that the C spec doesn't specify the result of a boolean
    > expression other than "zero means false, nonzero means true."

    No, *all* of C's operators that return a boolean result return either
    1 or 0. So the result of any of:

    (==,!=,<,>,<=,>=,!,&&,||)

    will never be anything but 1 or 0. This is also true in C++ (unless
    one of the operators is overloaded to do something else).

    --
    Clark S. Cox III
    <clarkcox3...>
  • On 8/30/07, Eyal Redler <eyredler...> wrote:
    > Hi,
    >
    > I'm not sure where and when but I recall reading something about ObjC
    > BOOL type values YES and NO not being the same as whet a boolean
    > expression might produce.
    > I can't recall the exact details but there was some kind of gotcha
    > related to this. For example, the expression
    >
    > BOOL myBool=(myInt==1);
    >
    > might set myBool differently then
    >
    > BOOL myBool=(myInt==1)?YES:NO;

    No, the two are identical.

    > Or maybe it was that
    >
    > if (myBool)
    > [foo bar];
    >
    > will perform differently then
    > if (myBool==YES)
    > [foo bar];

    This can behave differently (because BOOL is a typedef for char, and
    can therefore hold values other than 1 or 0). For example:

    BOOL myBool = 2;
    if(myBool==YES)
        [foo bar];

    though I'd advise against setting a BOOL to anything other than YES or NO.

    > Or maybe it was just that if I do this
    >
    > int myInt=4096;
    > BOOL myBool=myInt;
    >
    > Then if (myBool) will evaluate to false.

    In this case, it is undefined what myBool will evaluate to (unless
    you're on an odd system with char's that are greater than 13 bits).
    One way to ensure that integers are converted properly to boolean
    values is to prefix them with "!!", as in:

    int myInt=4096;
    BOOL myBool=!!myInt;

    At this point, myBool is guaranteed to be equal to 1.

    > This issue was bugging me for quite some time and I'm wondering if
    > somebody recalls this discussion.

    The general rule: Never compare directly to YES

    --
    Clark S. Cox III
    <clarkcox3...>
  • --- John Stiles <JStiles...> wrote:

    > I *think* that the C spec doesn't specify the result
    > of a boolean
    > expression other than "zero means false, nonzero
    > means true." So from
    > a "compliance with the spec" standpoint, the first
    > version might
    > return different results from the second version.

    I don't know about earlier versions, but the C99 spec
    guarantees that they will return 1 if true and 0 if
    not. The logic for determining whether something is
    true or false still boils down to "zero means false,
    nonzero means true," obviously, but the operators
    themselves can only return 1 or 0.

    Cheers,
    Chuck


    ____________________________________________________________________________________
    Be a better Globetrotter. Get better travel answers from someone who knows. Yahoo! Answers - Check it out.
    http://answers.yahoo.com/dir/?link=list&sid=396545469
  • I'd like to thank everybody for the informative answers.

    On Aug 31, 2007, at 21:28, Charles Steinman wrote:

    > --- John Stiles <JStiles...> wrote:
    >
    >> I *think* that the C spec doesn't specify the result
    >> of a boolean
    >> expression other than "zero means false, nonzero
    >> means true." So from
    >> a "compliance with the spec" standpoint, the first
    >> version might
    >> return different results from the second version.
    >
    > I don't know about earlier versions, but the C99 spec
    > guarantees that they will return 1 if true and 0 if
    > not. The logic for determining whether something is
    > true or false still boils down to "zero means false,
    > nonzero means true," obviously, but the operators
    > themselves can only return 1 or 0.
    >
    > Cheers,
    > Chuck
    >
    >
    >
    > ______________________________________________________________________
    > ______________
    > Be a better Globetrotter. Get better travel answers from someone
    > who knows. Yahoo! Answers - Check it out.
    > http://answers.yahoo.com/dir/?link=list&sid=396545469
  • > I don't know about earlier versions, but the C99 spec
    > guarantees that they will return 1 if true and 0 if
    > not. The logic for determining whether something is
    > true or false still boils down to "zero means false,
    > nonzero means true," obviously, but the operators
    > themselves can only return 1 or 0.

    Yes, and I do believe that logic applies to C much earlier than C99. The
    "gotcha" I think is that the actual bool type performs coercion such that it
    only ever holds true or false (0 or 1). So:

    bool foo = 42;
    if( foo == true )

    Takes the branch, exactly as:
    bool foo 42;
    if( foo )

    Would. However Objective-C BOOL, as pointed out before, is just a typedef
    for a char, and YES & NO are just typedefs, so there is no coercion, so:

    BOOL foo = 42;
    if( foo == YES )

    Would not take the branch.

    Of course there is no need to ever write "if( foo == YES )", so it's an easy
    problem to avoid--just learn that a variable of type bool/BOOL is an
    expression of type bool/BOOL and thus does not need to be compared to a
    bool/BOOL value in order to yield a bool/BOOL result.

    In other words, treat C like C, not like some >20 year-old obsolete version
    of Pascal or BASIC ;-)

    --
    Scott Ribe
    <scott_ribe...>
    http://www.killerbytes.com/
    (303) 722-0567 voice
  • On Sep 5, 2007, at 10:03 PM, Scott Ribe wrote:

    > However Objective-C BOOL, as pointed out before, is just a typedef
    > for a char, and YES & NO are just typedefs, so there is no
    > coercion, so:
    >
    > BOOL foo = 42;
    > if( foo == YES )
    >
    > Would not take the branch.
    >
    > Of course there is no need to ever write "if( foo == YES )", so
    > it's an easy
    > problem to avoid--just learn that a variable of type bool/BOOL is an
    > expression of type bool/BOOL and thus does not need to be compared
    > to a
    > bool/BOOL value in order to yield a bool/BOOL result.

    But a similar "gotcha" case is:

    BOOL isShift = [theEvent modifierFlags] & NSShiftKeyMask;
    if (isShift) {
    // this will never be hit, regardless of the state of the shift key!
    }

    This is because the result of the expression tests a single bit that
    happens to be higher than will fit in the BOOL, resulting in isShift
    always being 0.

    OTOH, either:

    if ([theEvent modifierFlags] & NSShiftKeyMask) {
    // this works
    }

    BOOL isShift = ([theEvent modifierFlags] & NSShiftKeyMask) !=
    NSShiftKeyMask; // explicitly test to see if that bit is set
    if (isShift) {
    // works as well
    }

    Glenn Andreas                      <gandreas...>
      <http://www.gandreas.com/> wicked fun!
    quadrium | flame : flame fractals & strange attractors : build,
    mutate, evolve, animate
  • On Sep 5, 2007, at 8:24 PM, glenn andreas wrote:

    >
    > On Sep 5, 2007, at 10:03 PM, Scott Ribe wrote:
    >
    >> However Objective-C BOOL, as pointed out before, is just a typedef
    >> for a char, and YES & NO are just typedefs, so there is no
    >> coercion, so:
    >>
    >> BOOL foo = 42;
    >> if( foo == YES )
    >>
    >> Would not take the branch.
    >>
    >> Of course there is no need to ever write "if( foo == YES )", so
    >> it's an easy
    >> problem to avoid--just learn that a variable of type bool/BOOL is an
    >> expression of type bool/BOOL and thus does not need to be compared
    >> to a
    >> bool/BOOL value in order to yield a bool/BOOL result.
    >
    >
    > But a similar "gotcha" case is:
    >
    >
    > BOOL isShift = [theEvent modifierFlags] & NSShiftKeyMask;
    > if (isShift) {
    > // this will never be hit, regardless of the state of the shift key!
    > }
    >
    >
    > This is because the result of the expression tests a single bit
    > that happens to be higher than will fit in the BOOL, resulting in
    > isShift always being 0.

    Wow, that's tricky. Does this generate a compiler warning? I would
    hope so, but you never know…

    > OTOH, either:
    >
    > if ([theEvent modifierFlags] & NSShiftKeyMask) {
    > // this works
    > }
    >
    > BOOL isShift = ([theEvent modifierFlags] & NSShiftKeyMask) !=
    > NSShiftKeyMask; // explicitly test to see if that bit is set
    > if (isShift) {
    > // works as well
    > }

    And option #3, for an ObjC++ programmer, would be to use the "bool"
    type instead of BOOL.
  • On 9/5/07, John Stiles <jstiles...> wrote:

    > And option #3, for an ObjC++ programmer, would be to use the "bool"
    > type instead of BOOL.

    As of C99, that option applies to plain ObjC (no ++) as well.

    --
    Clark S. Cox III
    <clarkcox3...>
  • El 6/9/2007, a las 5:24, glenn andreas escribió:

    > But a similar "gotcha" case is:
    >
    >
    > BOOL isShift = [theEvent modifierFlags] & NSShiftKeyMask;
    > if (isShift) {
    > // this will never be hit, regardless of the state of the shift key!
    > }
    >
    >
    > This is because the result of the expression tests a single bit
    > that happens to be higher than will fit in the BOOL, resulting in
    > isShift always being 0.

    I was once bitten by a similar bug when I foolishly wrote the
    following, thinking that the explicit cast was making my intentions
    clearer:

      (BOOL)(foo & bar)

    but when the value had only high-order bits set and was cast to BOOL
    it became 0 (NO). Although writing this without the cast would have
    worked:

      (foo & bar)

    I preferred to explicitly "normalize" the result:

      !!(foo & bar)

    Note that you can fall into the same trap even when the explicit cast
    isn't right there in front of your eyes:

    - (BOOL)test
    {
        return foo & bar;
    }

    In the method there is an implicit cast (the return of type BOOL)
    that can yield the same bug for appropriate values of foo and bar.
    This would be better:

    - (BOOL)test
    {
        return !!(foo & bar);
    }

    I wrote a short article about this on my weblog when I first ran into
    it:

      <http://wincent.com/a/about/wincent/weblog/archives/2007/05/
    clever_boolean.php
    >

    Probably something every programmar runs into once in their career
    and then never makes the same silly mistake again, hopefully. You
    gotta love C. Hehe.

    Cheers,
    Wincent
  • On 6 sep 2007, at 12.47, Wincent Colaiuta wrote:

    > In the method there is an implicit cast (the return of type BOOL)
    > that can yield the same bug for appropriate values of foo and bar.
    > This would be better:
    >
    > - (BOOL)test
    > {
    > return !!(foo & bar);
    > }

    My philosophy is: Be explicit. What you really have here is (and you
    point this out in your blog post):

    (0 != (foo & bar))

    Why is that so bad? Crystal clear and intention revealing. Still
    compact.
    I encourage the use of expressions that evaluate to a boolean value,
    and not relying on the "sort of implementation detail" of C where
    0==FALSE, and everything else is TRUE.

    If "-foo" is a method returning an object, don't write:

    if ([self foo])

    write:

    if (nil != [self foo])

    Now no one will mistake "-foo" from returning a boolean value. Don't
    "optimize" away a few characters while sacrificing readability and
    maintainability.

    Another example: If "-bar" is a method returning a boolean value,
    I've seen people use:

    (NO == [self bar])

    rather than:

    (![self bar])

    I can see why they choose the longer form, and I've recently started
    doing it myself. A little more typing, seemingly redundant even, but
    much more intention revealing - And that should take precedence.

    All a matter of opinion, obviously.

    Cheers,

    j o a r
  • > But a similar "gotcha" case is:
    >

    Yes, true, hadn't thought of that--or encountered it, since my habit is to
    use bool everywhere instead, because I learned C++ first.

    --
    Scott Ribe
    <scott_ribe...>
    http://www.killerbytes.com/
    (303) 722-0567 voice
previous month august 2007 next month
MTWTFSS
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
Go to today