Hex is easier to read and successfully write. Strings of 0s and 1s mean nothing.
"I want to set bits 1, 2 and 5", 0b00010011 or 0x13, for me it's the binary that's faster and clearer both to read and write. But I've never been good at hex conversion in my head.
As I said, where appropriate it's appropriate
However, that example is using magic bits and I think you would be better in that instance to go something like:
some_reg = BIT_1 | BIT_2 | BIT_5;
Not least because then you can go:
some_reg = BIT_1 //!< Invert display
| BIT_2 //!< 0,0 is top left
| BIT_5; //!< Interrupt on refresh
But then you'd probably be better going:
some_reg = DISP_INVERT
| DISP_ORIG_TOPLEFT
| DISP_REFR_INT_EN;
Just as examples - you particular circumstance would dictate, of course, but it illustrates that binary isn't necessarily what you want despite it being what you're thinking.
Fully agree. This makes the overall debate about numeric constants pointless, as they are, as you pointed out, the worst possible way of expressing set bits.
Writing constants the way you show above is the right way of approaching it IMO. Some languages have have better support for this than others, but through simple macros, as you showed, this is still very easy to do in C.
Sure it takes more "typing" than a single numeric constant. But, as we can see over and over again in discussions about programming languages, this is a moot point IMHO. I'll take readability and maintainability over saving a few keystrokes (while taking much more time to figure out the required constant in binary or hex...) You don't even save time by willing to save keystrokes here, it's usually even the contrary. Not to talk of course, about readability when you have to figure out later on what these bits are actually meaning.
Although I don't mind using macros in C, some languages do have better support for this. Two that come to mind are Oberon (and probably a couple of other Pascal derivatives), and Ada.
In Oberon, there is a SET type that allows to express "bit maps" easily. For instance, the above example with bits 1, 2 and 5 set would be: { 1..2, 5 }. Neat.
In Ada, you can "map" records to bit fields, but, contrary to C, with a guaranteed mapping with no ambiguity or implementation-dependant behavior.
That said, if you don't care about portability and know how bit fields are implemented on your particular platform/compiler, using bit fields in C is also an option.