1

I have flags

int A = 1;
int B = 2;
int C = 4;

I want to make a check, that only one flag can be specified to a function

check(A | B | C) ; // invalid
check(A); // valid
check(B); // valid
check(B | C); // invalid
void check(int flags) {
    // check that if A is specified, then B and C can't
    // check that if B is specified, then A and C can't
    // check that if C is specified, then B and A can't
}

How can I achieve this without tons of "if" statements?

Sergei Ledvanov
  • 2,131
  • 6
  • 29
  • 52

5 Answers5

6

To set the bit at position n, you want to set value 2^n.

So if you want to check that only one of the flags is specified, then you just want to ask if the number is a power of two.

And here is a question about how to do that: How to check if a number is a power of 2

As GrahamS says, you could read the question as saying that exactly one bit must be set (i.e. it can't be zero). So to do that, in addition, check that it's non-zero and that it's less than or equal to C.

Community
  • 1
  • 1
Joe
  • 46,419
  • 33
  • 155
  • 245
  • A power of two AND less than or equal to C. – GrahamS Jul 30 '13 at 12:43
  • I think if you try writing that function you'll find it is a lot more complicated that just directly checking the four valid values of `flags` as @MightyPork did. – GrahamS Jul 30 '13 at 12:49
  • Yes that's quite possible. But if you wrote a general function called `hasOneBitSet` you could re-use that. If you are never going to add more than 3 flags then checking them one by one would work. Age old tension between generality and real life. Appropriateness is up to individual circumstances. – Joe Jul 30 '13 at 12:54
2

Perhaps not the most elegant solution, but I think it should work:

bool check(int flags) {
    int A = 1;
    int B = 2;
    int C = 4;

    return 
        flags == 0 ||
        flags == A ||
        flags == B ||
        flags == C;
}
GrahamS
  • 9,980
  • 9
  • 49
  • 63
MightyPork
  • 18,270
  • 10
  • 79
  • 133
  • This seems by far the simplest solution to the question. Assuming that `A,B,C` are defined as constants elsewhere then it's a one-line function. – GrahamS Jul 30 '13 at 13:05
0

Why not use use an Enum for your flags, and check if the int value is defined in the enum.

enum Flags
{
    A = 1,
    B = 2,
    C = 4
}

void Check(int flags)
{
    bool isValid = Enum.IsDefined(typeof(Flags), flags);
    ...
}
Rubixus
  • 757
  • 4
  • 11
0

Here's an implementation with switch:

void check(int flags) {
  swicth (flags & (A | B | C)) {
    case A:
    case B:
    case C:
    case 0:
      return true;
    default:
      return false;
  }
}

It will work (in C#) only if A, B and C are literals, i.e. marked with const. Otherwise you can do the same with:

void check(int flags) {
  int relevantPart = flags & (A | B | C);
  return relevantPart == A || relevantPart == B || relevantPart == C || relevantPart == 0;
}

Otherwise use the power-of-two trick (from answer by Joe):

void check(int flags) {
  int relevantPart = flags & (A | B | C);
  return (relevantPart & (relevantPart - 1)) == 0;
}

I assumed there could be more significant bits than the three least bits, and they were to be ignored. I also assumed that neither A, B, nor C was valid (this is not co-existing in my interpretation).

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
0

I was going to comment but what GrahamS said was important enough to warrant some elaboration of the point.

Flags are generally used when you specifically want to be able to set multiples. Here is an example of a our task enum

namespace Shared.Enumerations
{
    [Flags]
    public enum TaskStatusEnum
    {
        NotSet = 0,
        Open = 1,
        Canceled = 2,
        Complete = 4,
        OnHold = 8,
        Inactive = 32,
        All = Open | Canceled | Complete | OnHold | Inactive
    }
} 

We do this so we can say give us any task that is open or on hold.

 TaskList activeTasks = taskListManager.TaskList.FindAll(target.Name, target.TaskType, (TaskStatusEnum.Open | TaskStatusEnum.OnHold));

Of course with a normal enumeration you can only set one thing at a time. You could actually do something like the following.

[TestMethod]
public void checkEnumVals()
{
        var ts = TaskStatusTestEnum.Open;
        ts |= TaskStatusTestEnum.OnHold;

        bool matchBoth = false;
        if ((ts & TaskStatusTestEnum.OnHold) == TaskStatusTestEnum.OnHold && (ts & TaskStatusTestEnum.Open) == TaskStatusTestEnum.Open)
           matchBoth = true;

        Assert.IsTrue(matchBoth);
}

I wouldn't suggest something like this though.

Kenn
  • 2,709
  • 2
  • 29
  • 60