Quick way to check a range of enum values
Is there a quick/oneline way of synatx weirdness that allows 开发者_StackOverflow中文版you to check if an enum has a value of the ones specified?
Example:
enum fruit_and_vegetables
{
apples,
pears,
tomatoes,
cucumbers
}
int main()
{
fruit_and_vegetables something = apples;
if( something = {apples, pears} ) // <-- this here
cout << "something is fruit." << endl;
else
cout "something is a vegetable." << endl;
return 0;
}
Thanks!
Not that I am aware of, but what you can do is assign values 2^i
to the enum members. For instance:
enum fruit_and_vegetables
{
apples = (1<<0),
pears = (1<<1),
tomatoes = (1<<2),
cucumbers = (1<<3)
// ...
}
Then you can check with
if (something & (apples | pears | tomatoes))
std::cout << "is tasty" << std::endl;
Of course, this is limited to enums with a reasonable size (I think you can have up to 32 elements).
EDIT
If you have more than 32 (64) values, you have to be more creative than this. By doing several checks, you can still be reasonably quick:
enum fruit_and_vegetables {
apples = 1, //!
pears,
tomatoes,
cucumbers,
// ...
grapes
}
#define FRUIT_AND_VEGETABLES 120
if ( (1<<something) & ((1<<apples) | (1<<pears) | (1<<tomatoes))
|| (1<<(something-32) & ((1<<(apples-32)) | (1<<(pears-32)) | (1<<(tomatoes-32))))
|| ...) {
std::cout << "my keyboard is broken, but tastes good" << std::endl;
}
But that is not really a great solution. If you have a large number of enums and they can be partitioned into several classes, then I would go with Noah Roberts' answer.
Ah, this can be done fairly easily...
template <typename T>
pair<T, fruit_and_vegetables> operator||(T t, fruit_and_vegetables v) {
return make_pair(t, v);
}
template <typename T>
bool operator==(fruit_and vegetables lhs, pair<T, fruit_and_vegetables> rhs) {
return lhs == rhs.second || lhs == rhs.first;
}
This can then be used like so:
if (something == (apple || pear || orange)) eat_the_yummy_fruit(something);
else feed_to_rabbit(something)
but won't work if you do (apple || (pear || orange))
. This can be fixed easily but I wanted to keep the code simple. I believe that this is the only answer so far that actually scales to large enums...
if (something < tomatoes)...
There is another way, that extends the answer of @bitmask:
Suppose there are a fixed number of criteria that you could check. So, instead of using bitmask for values of fruit_and_vegetables
enum (that will limit you to the size of the word), you can use additional LUT:
enum fruit_and_vegetables {
apples = 0,
pears,
tomatoes,
cucumbers
}
enum qualifs {
is_fruit = 1,
is_sweet = 1<<1,
is_round = 1<<2,
is_tasty = 1<<3
}
const qualifs qualifs_LUT[] = { // can be generated
is_fruit | is_sweet | is_round, // apple
...
};
so that checking for a specific qualifier will became
if (qualifs_LUT[tomato] & is_tasty)
EDIT: and another interesting method. Consider (again) the @bitmask: method. It relies on powers of 2. But how about primes? They grow much more slowly, so by assigning prime numbers to enum values, you could squize more values, assuming that product won't overflow:
enum fruit_and_vegetables {
apples = 2,
pears = 3,
tomatoes = 5,
cucumbers = 7
}
if ((apples * pears * tomatoes) % tomatoes == 0)
printf("it's tasty!");
this one is limiting the number of items in the control set.
You can write helper template which will help you to achieve the syntax which you want:
enum fruit_and_vegetables
{
nothing,
apples,
pears,
tomatoes,
cucumbers
};
// helper template
typedef fruit_and_vegetables fav;
template<fav v1 = nothing, fav v2 = nothing, fav v3 = nothing, fav v4 = nothing,
fav v5 = nothing, fav v6 = nothing, fav v7 = nothing, fav v8 = nothing>
bool check_equal( fruit_and_vegetables value )
{
return ( value == v1 || value == v2 || value == v3 || value == v4 ||
value == v5 || value == v6 || value == v7 || value == v8 );
}
// usage
int main()
{
fruit_and_vegetables something = apples;
if( check_equal<apples, pears>(something) )
std::cout << "something is fruit." << std::endl;
else
std::cout << "something is a vegetable." << std::endl;
return 0;
}
To handle larger, unsorted sets of produce:
enum fruit_and_vegetables
{
apples,
pears,
tomatoes,
cucumbers,
MAX_VALUE
};
vector<bool> arguablyVegetables(MAX_VALUE, false);
arguablyVegetables[tomatoes] = true;
arguablyVegetables[cucumbers] = true;
cout << arguablyVegetables[apples] << endl;
why not use a set< fruit_and_vegetables >
for logical-or-ing several fruit_and_vegetables
?
if you put constexpr
before the operator ||
and operator ==
overloads,
as well as before the arguments and result types,
then the compiler will evaluate your code at compile time (no runtime overhead)
if possible ;)
Scales well to bigger a || b || c || c
enum ranges and you can put the values in brackets as you please.
#include <set>
using std::set;
enum fruit_and_vegetables
{
apples,
pears,
tomatoes,
cucumbers
};
set< fruit_and_vegetables > operator||( fruit_and_vegetables left, fruit_and_vegetables right ) {
set< fruit_and_vegetables > set;
set.insert( left );
set.insert( right );
return set;
}
set< fruit_and_vegetables > operator||( set<fruit_and_vegetables> left, fruit_and_vegetables right ) {
left.insert( right );
return left;
}
set< fruit_and_vegetables > operator||( fruit_and_vegetables left, set<fruit_and_vegetables> right ) {
right.insert( left );
return right;
}
bool operator!=( fruit_and_vegetables lhs, set<fruit_and_vegetables> rhs ) {
return ( rhs.find( lhs ) == rhs.end() );
}
bool operator==( fruit_and_vegetables lhs, set<fruit_and_vegetables> rhs ) {
return !( lhs != rhs );
}
int main() {
fruit_and_vegetables fav = apples;
if ( fav == ( apples || (pears || tomatoes) ) ) cout << "match apples\n";
fav = cucumbers;
if ( fav == ( (apples || pears) || tomatoes ) ) cout << "Error! matched ghost cucumbers\n";
if ( fav != apples ) cout << "correct no match apples\n";
if ( fav == cucumbers ) cout << "default operator==(FaV, FaV) match\n";
if ( fav == ( pears || apples ) ) cout << "yummi\n";
return 0;
}
精彩评论