views:

166

answers:

6
int bits = 0;
bool a = true, b = false, c = true;  // 101 = 5

bits = bits | a << 2;
bits = bits | b << 1;
bits = bits | c;

cout << bits;

This is the code I am going to use to take a set of three booleans and convert it into an int for a switch statement. I have 8 cases based on the combined state of these 3 booleans. Am I doing this right? Right, not in the sense of the syntax although if there are any problems there please advise. More right in the sense of "Is this the best way to solve this problem?"

+4  A: 

If you are using C++, you could use bitset<N>.

grddev
A: 

You can always do the conversion explicitly:

bits = bits | (a ? 1 : 0) << 2;

However I believe C/C++ will handle it implicitly, once you're using the bit-shift operator.

You should define constants for each flag, giving a name to the bit you are setting:

const int a_flag = 2;
bits = bits | (a ? 1 : 0) << a_flag;

Edit:

As you can see from the comments on my answer, most C/C++ programmers prefer to learn implicit conversions and operator precedence. Therefore your original code is "most correct".

Edit 2:

Except, you should use the |= operator:

const int a_flag = 2;
bits |= a << a_flag;
Merlyn Morgan-Graham
In C++, `bool` is an integer type that has values 0 or 1, so being explicit is pointless. (And using the ternary operator would kind of defeat the point of using `switch` instead of a sequence of `if` statements.)
jamesdlin
`(a ? 1 : 0)` means `(a != 0)`
wilhelmtell
@wilhelmtell: What it "means" is what the code says. Meaning is defined in terms of what the human reads. What it is logically substitutable with doesn't really describe the intent of the programmer. Also, what code compiles to is a different story :) The compiled code doesn't have a concept of booleans, just constrained integer values.
Merlyn Morgan-Graham
@jamesdlin: Except for compiler optimizations. I don't know what common compiler optimizations are, but these should be an obvious case, and end up as the same code. Same as with something like `some_integer_variable / 2` being converted to a bit-shift.
Merlyn Morgan-Graham
@wilhelmtell: However, I agree that most C/C++ programmers prefer the coder to understand implicit conversions and operator precedence, so would consider his original code closer to the correct style.
Merlyn Morgan-Graham
@jamesdlin: No, it is not some integer type, it is it's own type. (It is not `typedef int bool;`, though it is an *integral* type.) Nor does it take on the values 0 or 1 (like `const int true = 1`), it takes on either `true` or `false`, which are their own boolean values. However, a `bool` can be implicitly converted to an integer via `r = b ? 1 : 0;`.
GMan
@GMan: Yes, I meant "integer type" as "integral type", not `int`. In my defense, section 3.9.1/7 of the ISO C++98 standard does say that "A synonym for integral type is *integer type* ". And yeah, the "has values 0 or 1" thing was supposed to be with respect to integer promotion. Oops.
jamesdlin
@james: Ah, didn't know that, fair point. :)
GMan
@GMan: Good to know my bit-rotting memory on this aspect of C++ is still correct :)
Merlyn Morgan-Graham
+1  A: 

You're doing it right. You could make the code a little more succinct though:

bits |= (a<<2) | (b<<1) | (c<<0);

Just be aware that the Standard doesn't enforce any size constraint on bool. Pragmatically speaking there shouldn't be a problem with three bits, but the Standard doesn't back you here.

wilhelmtell
Why would a size constraint on `bool` matter? `b << i` should result in an integer.
Brian S
`b << i` results in an integral type of the left operand's type. See 5.8/1.
wilhelmtell
A: 

I would do it as:

bits = (bits << 1) | a;
bits = (bits << 1) | b;
bits = (bits << 1) | c;

which would require less maintenance work if you needed to add or to remove a flag.

However, doing this so that you can use it for a switch sounds like it might be a bad idea. Adding a flag would double the number of states you'd need to handle, and the case values would be fragile and hard to read.

But if you really must, here's another approach:

enum
{
    c_bit_offset,
    b_bit_offset,
    a_bit_offset
};

unsigned int bits =   (a << a_bit_offset)
                    | (b << b_bit_offset)
                    | (c << c_bit_offset);
switch (bits)
{
    case 0:
       /* Do something. */
       break;
    case (1 << a_bit_offset):
       /* Do something. */
       break;
    case (1 << a_bit_offset) | (1 << b_bit_offset):
       /* Do something. */
       break;
    ...
}

By the way, you probably should use unsigned int instead of int.

jamesdlin
+1  A: 

$ cat ttt.c

//example of C solution
#include <stdio.h>

int main() {
        union {
                unsigned int val;
                struct {
                        unsigned a : 1;
                        unsigned b : 1;
                        unsigned c : 1;
                        //unsigned d : 1; 
                        //e, f, g, h...
                } flags;
        } bits;

        bits.val=0;
        bits.flags.a = 1;
        bits.flags.c = 1;

        printf("val: %d\n",bits.val);
        return 0;
}

~$ ./ttt

val: 5
hlynur
Technically, writing one value of a `union` and then reading another one is invoking undefined behavior.
sbi
Great! I'd forgot how unions can make bit operations so easy! Just an question, does unions increase the code size compared with the "define" option?
RMAAlmeida
I too have fallen prey for unions. In Soviet Russia they say "you don't use The Union; The Union uses you". For the sake of efficiency the C++ Standard allows compiler implementers to align structs and unions as they see fit. This, on the expense of allowing us simpletons to rely on a member lying in a specific address in memory. What this implies in the case of unions, as @sbi correctly notes, is that you can't write into one member and then read from another. The start-address of the member you wrote into may not be what you expect.Alas, this program is incorrect.
wilhelmtell
@sbi, wilhelmtell - so, what's the point with unions then, if alignment of the members is not guaranteed? If you can't write to one member and read from the other?? I remember back in the days an example for using unions was with registers and half-registers (`union REGS`). We had a union with 2 members, one is a register and the other a struct of two half registers. You could then use an object of this type to do interact with system calls conveniently. But then, this was C. Is this a different between C and C++?
ysap
I don't remember off the top of my head (read: I'm too lazy right now to google and find out) what the C standard says. But I'm fairly confident this is something C and C++ share. The idea of unions is to share memory between two or more data, when you know you never need them at the same time. You can't use a union for two distinct tasks at the same time. When you use a union member you must assume the other member(s) don't and never did exist. In other words, when you write into one member, don't read from the other -- it doesn't exist. When you write into the other, don't read from first.
wilhelmtell
For a given union `union u { int d; int* n };` you make a "switch" in your mind when you decide to stop using `d` and start using `n`. One example for a proper use of a union is for a memory pool. You start a program and allocate a large chunk of memory, called "the memory pool". Your allocations from now on are a game of give-and-take from the pool. If the pool runs low you request more from the OS; if the OS says no then the pool gives you time and resources to warn the user.
wilhelmtell
It would be expensive to maintain a contiguous array as the pool, so instead we use a linked list. Normally the linked list would be expensive in space, because each small chunk of memory needs a pointer to point to the next byte in the pool. That's where the union comes in. The pool is a pool of pointer-sized chunks, each a union in one of two states: a link in the pool or an allocated memory. If it's a link in the pool we use the the `next` pointer member of the union. If it's an allocated memory we use the data part of the union. This way, there is zero overhead in terms of speed and space.
wilhelmtell
A: 

You can make some defines to make easy to work with bits

#define BitSet(arg,bit) ((arg) |= (1<<bit))
#define BitClr(arg,bit) ((arg) &= ~(1<<bit)) 
#define BitFlp(arg,bit) ((arg) ^= (1<<bit)) 
#define BitTst(arg,bit) ((arg) & (1<<bit)) 

Then you can use only one char

keys = 0b00000101;
BitSet (keys,1);

This an common way to work in embedded systems.

RMAAlmeida