开发者

Array with flags

Let say I have an array of bool flags, it would be set to true or开发者_如何学编程 false depends on the condition.

Let say the index 1 ,2 ,6 have been set and all other flags are not set, I need to call functionA, and if index 2,3, 5 have been set and all other flags are not set, I need to call functionB. Is there an easy way where I could perform the logic above other than doing this:

if(array[1] == true && array[2] == true && array[6] == true && 
   array[3] == false && array[4] == false && array[5] == false)
{
  functionA();
}


Maintenance and readability nightmare!

Consider this instead:

bool temperature_sensor_tripped(const bool_array& flags)
{
     return flags[1];
}

// [...]

if (temperature_sensor_tripped(array) 
    && moisture_sensor_tripped(array)
    && !alarm_dispatched(array))
{
    functionA();
}

This has the advantage that moisture_sensor_tripped() and its kin can be called from other functions without you (or the maintainer) remembering the order of the flags.


Suggestion:

bool truecondition = array[1] && array[2] && array[6];
bool falsecondition = !array[3] && !array[4] && !array[5];

if (truecondition && falsecondition)
{
//do something
}


You can skip the unnecessary == true comparisons and use ! for the false cases:

if (array[1] && array[2] && array[6] && !array[3] && !array[4] && !array[5])

Alternatively, make a bool array and compare against that:

bool condition[6] = { true, true, false, false, false, true };
if (std::equal(array+1, array+7, condition)) { // +1 for your 1-based indexes


Instead of bool, you can use char instead, and treat each bit as one flag. Say this char flags (which is actually a combination of many flags) is this:

char flags;

So if index 1,2,6 are set, that means

//index count starts from rightmost bit
flags = 0100 0110 (binary) =  64 + 4 + 2 = 70 (decimal)

Similarly, index 2,3,5 set means

flags = 0010 1100 (binary) =  32 + 8 + 4 = 44 (decimal)

So you can write

if (flags == 70 ) //i.e when index 1,2,6 are set
{
     functionA();
}
else if ( flags == 44 ) //i.e when index 2,3,5 are set
{
    functionB();
}

As size of char is one byte, so you can have at most 8 flags in one variable (assuming CHAR_BIT is 8 on your machine which is most likely to be true). But you need more flags, then you can take int instead, and work accordingly.


Well, that is the basic idea. Now you can improve that as follows:

enum flag
{
     flag0 = 1 << 0, //1   = 0000 0001
     flag1 = 1 << 1, //2   = 0000 0010
     flag2 = 1 << 2, //4   = 0000 0100
     flag3 = 1 << 3, //8   = 0000 1000
     flag4 = 1 << 4, //16  = 0001 0000
     flag5 = 1 << 5, //32  = 0010 0000
     flag6 = 1 << 6, //64  = 0100 0000
     flag7 = 1 << 7, //128 = 1000 0000
};

char flags = 0 ;

//this is how you can set flags!
flags |= flag1 ;         //set index 1        (one at a time)
flags |= flag2 | flag6 ; //set index 2 and 6  (more than one at a time)

if ( flags == (flag1 | flag2 | flag6) )
{
      functionA();
} 
else if ( flags == (flag2 | flag3 | flag5) )
{
      functionB();
} 

//and this is how you can unset flags!
flags &= ~flag1 ;          //unset index 1        (one at a time)
flags &= ~flag2 & ~flag6 ; //unset index 2 and 6  (more than one at a time)

See this online demo : http://www.ideone.com/6BdGf


You could go with std::bitset if the array has a fixed size and test with simple masks:

#include <bitset>

void funcA(){
}

void funcB(){
}

enum FuncMasks{
  funcA_Mask = 0x23, // 0010 0011
  funcB_Mask = 0x16, // 0001 0110
};

int main(){
  std::bitset<6> flags;

  if(flags.to_ulong() & funcA_Mask)
    funcA();
  else if(flags.to_ulong() & funcB_Mask)
    funcB();
}


The easier way would be using an integer for all those flags and then using bit operations. You could as well use std::bitset in case you're looking for a ready to be used solution.

enum flags { // i use hexadecimal notation as it's easier to see unused flags - using 8 bits/1 byte is enough for you but you could extend it even more as well
    FLAG_NONE  = 0x00;
    FLAG_ONE   = 0x01;
    FLAG_TWO   = 0x02;
    FLAG_THREE = 0x04;
    // ...
};

// To set some flags you use bitwise operators:
char flags_set = FLAG_ONE | FLAG_THREE | FLAG_SIX;
flags_set |= FLAG_FOUR; // add fourth flag
flags_set &= ~FLAG_TWO; // remove second flag (if set)

// Similar way you can check the flags and you've got "talking" code that's a lot easier to understand than tons of if()s
if (flags_set & (FLAG_ONE | FLAG_TWO | FLAG_SIX))
    functionA();
elseif (flags_set & (FLAG_TWO | FLAG_THREE | FLAG_FIVE))
    functionB();


Here is an alternative using valarray:

#include <valarray>
#include <iostream>

bool all(const std::valarray<bool> & va) {
  return va.min();
}

bool none(const std::valarray<bool> & va) {
  return !va.max();
}

int main() {
  bool bits_init[] = {0, 1, 1, 0, 0, 0, 1};
  std::valarray<bool> bits(bits_init, 7);
  size_t true_bits_init[] = {1, 2, 6};
  std::valarray<size_t> true_bits(true_bits_init, 3);
  size_t false_bits_init[] = {0, 3, 4, 5};
  std::valarray<size_t> false_bits(false_bits_init, 4);

  if(all(bits[true_bits]) && none(bits[false_bits]))
    std::cout << "functionA" << std::endl;
  else
    std::cout << "functionB" << std::endl;
  bits[2] = false;
  if(all(bits[true_bits]) && none(bits[false_bits]))
    std::cout << "functionA" << std::endl;
  else
    std::cout << "functionB" << std::endl;
  bits[2] = true;
  bits[3] = true;
  if(all(bits[true_bits]) && none(bits[false_bits]))
    std::cout << "functionA" << std::endl;
  else
    std::cout << "functionB" << std::endl;
}

I don't know if it is what you need. It does provide a lot of flexibility if the size of your arrays varies.


If the functions have a return type that can be evaluated in a boolean context (e.g. bool, int, double, pointer), you can write:

Array& a = array; // for brevity...
a[1] && a[2] && a[6] && functionA() ||
a[2] && a[3] && a[5] && functionB() ||
...

You may or may not prefer this && chaining of functions to the explicit "if" usage (the style above's popular with some perl hackers, FWIW). The other aspects the reference, which is a good idea for really repetitive code.

If the functions lack such a return type, you can use a wrapper with a return type to call them.

Alternatively, rather than having a plain array you can make it an object with operator[] providing indexed access, then add functionality to it allowing:

array.run_if(1, 2, 6, functionA)
     .run_if(2, 3, 5, functionB);

Here, the main issue is how to handle different numbers of indices. Options include:

  • overload for 1, 2, 3, 4 etc. indices, assuming the upper limit's manageable
  • use a preprocessor facility to automatically generate a large but finite set of overloads (see Loki or boost for examples)
  • put the function first, then use ... and stdargs, with a sentinel value such as -1 (no arbitrary limit, but more error prone)
  • accept indices in a std::vector<>, array (which a template can bind to, discovering the number of elements, though C++ lacks an "array literal" so you need a named variable to pass rather than run_if([1, 2, 6], functionA).

As illustrated, the chaining of run_if calls suggests run_if return a reference to *this, but this implies continued matching and potential function calls after the first match. Some object state would need to track and prevent this.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜