BOOLing for Dollars

While we’re all aiting-way or-fay eopard-Lay, I’d like to share a pointer that I picked up while mugging a C library. (I have no idea what that means. It seemed witty when I wrote it.) As you know, I’m always ahead of the curve, setting the trends, framing the public discourse. Thus, I should add my 1.2 cents — the dollar is weak, and I’m a little short this month — on a hot topic discussed on the Cocoa-dev mailing list recently (in geological time, anyway): the use of the Objective-C BOOL type.

If you look in the header file /usr/include/objc/objc.h, you can see how BOOL is defined:


	typedef signed char		BOOL;
	// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C"
	// even if -funsigned-char is used.

	#define YES             (BOOL)1
	#define NO              (BOOL)0

A char type — e.g., char, signed char, unsigned char — is always one byte, i.e., sizeof(signed char) == 1, whereas in most implementations an int type is more than one byte. A byte standardly consists of 8 bits, or 12 nibbles. What happens to the extra bits if you convert an int into a BOOL? According to the wacky rules of C type conversion, the result is implementation-dependent. Many implementations simply throw away the highest bits. (Other implementations recycle them into information superhighway speed bumps.) As a consequence, it’s possible that myIntVar != 0 && (BOOL)myIntVar == NO.

Usually we don’t have to worry about this, because ‘boolean’ operators in C, such as == and !, always return 1 or 0. When we use bitwise operators, on the other hand, the problem does come into play. Suppose, for example, that we’re testing whether the option key is down. The method -[NSEvent modifierFlags] returns a bit field indicating the modifier keys that are pressed, and bit masks can be used to test for specific keys. Consider the following code; there are situations where doSomethingAfterEvent: does something, yet doSomethingElseAfterEvent: does nothing.


	-(void) doSomethingAfterEvent:(NSEvent *)anEvent
	{
		if (anEvent)
		{
			if ([anEvent modifierFlags] & NSAlternateKeyMask)
			{
				[self doSomething];
			}
		}
	}

	-(void) doSomethingElseAfterEvent:(NSEvent *)anEvent
	{
		if (anEvent)
		{
			BOOL shouldDoSomethingElse = [anEvent modifierFlags] & NSAlternateKeyMask;
			if (shouldDoSomethingElse)
			{
				[self doSomethingElse];
			}
		}
	}

It has been suggested on the mailing list that the type conversion could be handled by


	BOOL shouldDoSomethingElse = !!([anEvent modifierFlags] & NSAlternateKeyMask);

or


	BOOL shouldDoSomethingElse = ([anEvent modifierFlags] & NSAlternateKeyMask) != 0;

However, these approaches would only work for single-bit masks. What if we wanted to test both the option key and the shift key?

The point I wish to make here actually has little to do with the BOOL type. (Say what?!?) Bitwise operators are not boolean operators. A boolean operator only returns 1 or 0. A bitwise operator, in contrast, can return any bit field. The proper way to handle a bitmask is to test whether the resulting bit field has the desired value:


	unsigned int myMask = NSAlternateKeyMask | NSShiftKeyMask;
	BOOL isMyKeyComboPressed = ([anEvent modifierFlags] & myMask) == myMask;

Yes, I know Robot Chicken already covered this subject a ha-while ago. I didn’t really care for it.

7 Responses to “BOOLing for Dollars”

  1. Adrian Bool says:

    1 Byte contains 2 Nibbles. I guess the ’12′ is a typo?

  2. Jeff says:

    You are correct. Nice name, by the way.

  3. Peter Hosey says:

    A bitwise operator, in contrast, can return any bit field.

    I believe you meant bit mask here. A bit field is a specific pattern of structure members, like this:

    unsigned reserved: 5;
    unsigned hallLightSwitches: 2;
    unsigned masterBedroomLightSwitch: 1;
    unsigned childBedroomLightSwitch: 1;
    unsigned guestBedroomLightSwitch: 1;
    unsigned bathroomLightSwitch0: 1;
    unsigned bathroomLightSwitch1: 1;
    unsigned livingRoomLightSwitch: 1;
    unsigned diningRoomLightSwitch: 1;
    unsigned kitchenLightSwitch: 1;
    unsigned garageLightSwitch: 1;

  4. That just shows, that Apple should have stuck to int, the C way. BOOL is a stupid useless construct, that in the end just created problems.

  5. Anonymous says:

    AC: “That just shows, that Apple should have stuck to int, the C way. BOOL is a stupid useless construct, that in the end just created problems.”

    Not really. You still have the same problem with int if the result of the bitwise operation is larger than an int. Using the result of a bitwise operation as a Boolean value is risky and needlessly terse at best. Take the extra time to write what you really mean, as the author suggests.

  6. [...] From Lap Cat Software Blog: A char type – e.g., char, signed char, unsigned char – is always one byte, i.e., sizeof(signed char) == 1, whereas in most implementations an int type is more than one byte. A byte standardly consists of 8 bits, or 12 nibbles. What happens to the extra bits if you convert an int into a BOOL? According to the wacky rules of C type conversion, the result is implementation-dependent. Many implementations simply throw away the highest bits. (Other implementations recycle them into information superhighway speed bumps.) As a consequence, it’s possible that myIntVar != 0 && (BOOL)myIntVar == NO. [...]

  7. Michael says:

    When was this covered on Robot Chicken? Nice Family Guy reference at the end.