Comments on: Understanding your bool type Yes, you can do that with normal bools. What I tried to point out first was that true would always be the value 0x01, and knowing that you know you can use the '&' operator. You can use your own bool type saving casting/normalizations but then you need to guarantee that true would always be one there. Maybe that became a little bit confusing in the article. Yes, you can do that with normal bools.

What I tried to point out first was that true would always be the value 0×01, and knowing that you know you can use the ‘&’ operator. You can use your own bool type saving casting/normalizations but then you need to guarantee that true would always be one there.

Maybe that became a little bit confusing in the article.

]]>
By: Dan Olson/2011/04/18/understanding-your-bool-type/#comment-3426 Dan Olson Sun, 01 May 2011 10:52:50 +0000 Just to point out, short circuit evaluation like this is part of the C++ standard. So unless you overload || and && on your own types, a standards compliant compiler will always branch. Just to point out, short circuit evaluation like this is part of the C++ standard. So unless you overload || and && on your own types, a standards compliant compiler will always branch.

]]>
By: Jesse Cluff/2011/04/18/understanding-your-bool-type/#comment-3031 Jesse Cluff Wed, 20 Apr 2011 21:18:13 +0000 It's not a case of optimization. When you have a() && b(), the second operand can only be evaluated if the first one returned true. Therefore, it must execute a() first, wait for it to finish, and then, if a() returned true b() can be evaluated. This is a completely sequential process, and requires a test/branch to know if b() requires to be executed, so there isn't much the compiler can do. From MSDN: "The first operand is completely evaluated and all side effects are completed before continuing evaluation of the logical AND expression. The second operand is evaluated only if the first operand evaluates to true (nonzero)." You can find more information here: http://msdn.microsoft.com/en-us/library/c6s3h5a7%28v=vs.71%29.aspx It’s not a case of optimization.

When you have a() && b(), the second operand can only be evaluated if the first one returned true. Therefore, it must execute a() first, wait for it to finish, and then, if a() returned true b() can be evaluated. This is a completely sequential process, and requires a test/branch to know if b() requires to be executed, so there isn’t much the compiler can do.

From MSDN: “The first operand is completely evaluated and all side effects are completed before continuing evaluation of the logical AND expression. The second operand is evaluated only if the first operand evaluates to true (nonzero).”

You can find more information here:
I'd be very surprised if the composite && example you gave would in reality generate many branches. With full optimization on have you checked the assembly for proof that branches are generated for every component of that if for Visual Studio and gcc? I’d be very surprised if the composite && example you gave would in reality generate many branches. With full optimization on have you checked the assembly for proof that branches are generated for every component of that if for Visual Studio and gcc?

]]>
By: Bruno Evangelista/2011/04/18/understanding-your-bool-type/#comment-3000 Bruno Evangelista Tue, 19 Apr 2011 22:47:12 +0000 Wow, I've never tried signed bools before... Thanks for sharing that. =) In VS2010 I get this warning: C4076: 'signed': can not be used with type 'bool'. However, it does print "f1" and "f2"... This makes me think that the warning message is misleading. Wow, I’ve never tried signed bools before… Thanks for sharing that. =)

In VS2010 I get this warning: C4076: ‘signed’: can not be used with type ‘bool’. However, it does print “f1″ and “f2″… This makes me think that the warning message is misleading.

]]>
By: Bruno Evangelista/2011/04/18/understanding-your-bool-type/#comment-2997 Bruno Evangelista Tue, 19 Apr 2011 22:34:58 +0000 I'll tell you a bit more fun about 'bool's. Try this code: <code> void f( bool b ) { cout << "f1" << endl; } void f( signed bool b ) { cout << "f2" << endl; } void main() { f( true ); // prints "f1" f( -true ); // prints "f2" } </code> I’ll tell you a bit more fun about ‘bool’s. Try this code:


void f( bool b )
{
cout << "f1" << endl;
}

void f( signed bool b )
{
cout << "f2" << endl;
}

void main()
{
f( true ); // prints "f1"
f( -true ); // prints "f2"
}

]]>
By: Reavenk/2011/04/18/understanding-your-bool-type/#comment-2982 Reavenk Tue, 19 Apr 2011 17:07:30 +0000 The other undefined behaviour which can occur from replacing your logical operators with bitwise ones in C or C++ is related to the point you mention with losing the short-circuiting, but is subtle enough that I've seen it catch people out. It's one of those things that is probably fine in an unoptimized build, but can break as soon as you turn on compiler optimization (at which point the compiler often gets blamed!). The following has defined and predictable behaviour: a = b() || c() || d(); This one might not though: a = b() | c() | d(); as the order that the terms are evaluated is undefined. b() might be called before c() or after it, or before or after d(). The reason is that '||' is a sequence point. Anything to the left of it is guaranteed to be evaluted before anything on the right of it (hence, we can short-circuit). '|' is not a sequence point. The compiler can evalute the terms in whichever order it likes. If there is any reliance in your code in the order of this evaluation, then you've immediately got a potentially nasty bug that may disappear/reappear depending on a number of different factors (code changes near it, optimization level, etc). Much safer to write as: b = b(); c = c(); d = d(); a = b | c | d; The semicolons obviously act as sequence points, so you can rely on the order of evaluation. The other undefined behaviour which can occur from replacing your logical operators with bitwise ones in C or C++ is related to the point you mention with losing the short-circuiting, but is subtle enough that I’ve seen it catch people out. It’s one of those things that is probably fine in an unoptimized build, but can break as soon as you turn on compiler optimization (at which point the compiler often gets blamed!).

The following has defined and predictable behaviour:

a = b() || c() || d();

This one might not though:

a = b() | c() | d();

as the order that the terms are evaluated is undefined. b() might be called before c() or after it, or before or after d(). The reason is that ‘||’ is a sequence point. Anything to the left of it is guaranteed to be evaluted before anything on the right of it (hence, we can short-circuit). ‘|’ is not a sequence point. The compiler can evalute the terms in whichever order it likes. If there is any reliance in your code in the order of this evaluation, then you’ve immediately got a potentially nasty bug that may disappear/reappear depending on a number of different factors (code changes near it, optimization level, etc).

Much safer to write as:

b = b();
c = c();
d = d();
a = b | c | d;

The semicolons obviously act as sequence points, so you can rely on the order of evaluation.

]]>
By: Greg Bedwell/2011/04/18/understanding-your-bool-type/#comment-2979 Greg Bedwell Tue, 19 Apr 2011 15:33:26 +0000

]]>
By: Vilya/2011/04/18/understanding-your-bool-type/#comment-2972 Vilya Tue, 19 Apr 2011 12:25:17 +0000 Actually, in all seriousness, whilst your final comment about the speed implications of short-circuiting are interesting, I not convinced the potential danger of obscure code is worth the effort. Whilst your example of getResult2(32, 48) works fine, there are many more examples that don't work, e.g. getResult2(32, 31). There are very good times when bitfields should be used, but they should be done with known constants so that it's obvious what the code is doing. Applying a blanket policy that "true is any non-zero value" and then suggesting that "& is a perfectly good substitute for &&" is asking for trouble. On the other hand, there are many times when using bitfield logic is great, e.g. bits representing different things and then casting to a bool to check if they're all 0 or !~x to see if all bits of x are set, etc. I'd say that these are advanced cases though, and should be surrounded by copious amounts of comments so that everyone on your team knows what's going on. Actually, in all seriousness, whilst your final comment about the speed implications of short-circuiting are interesting, I not convinced the potential danger of obscure code is worth the effort. Whilst your example of getResult2(32, 48) works fine, there are many more examples that don’t work, e.g. getResult2(32, 31).

There are very good times when bitfields should be used, but they should be done with known constants so that it’s obvious what the code is doing. Applying a blanket policy that “true is any non-zero value” and then suggesting that “& is a perfectly good substitute for &&” is asking for trouble. On the other hand, there are many times when using bitfield logic is great, e.g. bits representing different things and then casting to a bool to check if they’re all 0 or !~x to see if all bits of x are set, etc. I’d say that these are advanced cases though, and should be surrounded by copious amounts of comments so that everyone on your team knows what’s going on.

]]>
By: Ranulf Doswell/2011/04/18/understanding-your-bool-type/#comment-2968 Ranulf Doswell Tue, 19 Apr 2011 11:14:37 +0000 I think it should be obvious that the second case has a different meaning. Clearly you can't go through your entire project and replace your logical ands with bitwise ands. However, you do present an excellent solution for dealing with many branches. In the best case scenario, your function calls will be inlined, and all of the bitwise ops performed in parallel through streaming, culminating in a single branch, which is clearly better. This is definitely a practice that everyone should be doing, if they are not already. I think it should be obvious that the second case has a different meaning. Clearly you can’t go through your entire project and replace your logical ands with bitwise ands.

However, you do present an excellent solution for dealing with many branches. In the best case scenario, your function calls will be inlined, and all of the bitwise ops performed in parallel through streaming, culminating in a single branch, which is clearly better. This is definitely a practice that everyone should be doing, if they are not already.

]]>