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
...
andstdargs
, 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 thanrun_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.
精彩评论