A compact way to write if (..) with many equalities

Is there a better way to write code like this:

if (var == "first case" or var == "second case" or var == "third case" or ...) 

In Python, I can write:

 if var in ("first case", "second case", "third case", ...) 

which also gives me the ability to easily convey a list of good options:

 good_values = "first case", "second case", "third case" if var in good_values 

This is just an example: the var type may differ from the string, but I'm only interested in alternative ( or ) comparisons ( == ). var may not be const , but the list of parameters is known at compile time.

About the bonus:

  • laziness or
  • reversal of the compilation cycle
  • easily extends to other operators than ==
+53
c ++ if-statement
Mar 14 '16 at 9:49
source share
9 answers

if you want to deploy this compile time you can use something like this

 template<class T1, class T2> bool isin(T1&& t1, T2&& t2) { return t1 == t2; } template<class T1, class T2, class... Ts> bool isin(T1&& t1 , T2&& t2, T2&&... ts) { return t1 == t2 || isin(t1, ts...); } std::string my_var = ...; // somewhere in the code ... bool b = isin(my_var, "fun", "gun", "hun"); 

I have not actually tested this, and the idea comes from Alexandrescu. Variadic patterns are fun conversations. So for details (and proper implementation) see this.

Edit: in C ++ 17 they made a nice fold expression syntax

 template<typename... Args> bool all(Args... args) { return (... && args); } bool b = all(true, true, true, false); // within all(), the unary left fold expands as // return ((true && true) && true) && false; // b is false 
+43
Mar 14 '16 at 10:07
source share

any_of algorithm can work quite well:

 #include <algorithm> #include <initializer_list> auto tokens = { "abc", "def", "ghi" }; bool b = std::any_of(tokens.begin(), tokens.end(), [&var](const char * s) { return s == var; }); 

(You can limit the tokens area to the minimum necessary context.)

Or you create a shell template:

 #include <algorithm> #include <initializer_list> #include <utility> template <typename T, typename F> bool any_of_c(const std::initializer_list<T> & il, F && f) { return std::any_of(il.begin(), il.end(), std::forward<F>(f)); } 

Using:

 bool b = any_of_c({"abc", "def", "ghi"}, [&var](const char * s) { return s == var; }); 
+26
Mar 14 '16 at 10:03
source share

So you want a radical change in language . In particular, you want to create your own operator. Are you ready?

Syntax

I want to change the syntax to use a list in the style of C and C ++:

 if (x in {x0, ...}) ... 

In addition, we will add our new operator in to any container for which begin() and end() are defined:

 if (x in my_vector) ... 

There is one caveat: this is not a true statement, so it should always be enclosed in brackets as its own expression:

 bool ok = (x in my_array); my_function( (x in some_sequence) ); 

The code

The first thing to know is that RLM often requires the abuse of macros and operators. Fortunately, for a simple predicate for members, abuse is actually not so bad.

 #ifndef DUTHOMHAS_IN_OPERATOR_HPP #define DUTHOMHAS_IN_OPERATOR_HPP #include <algorithm> #include <initializer_list> #include <iterator> #include <type_traits> #include <vector> //---------------------------------------------------------------------------- // The 'in' operator is magically defined to operate on any container you give it #define in , in_container() = //---------------------------------------------------------------------------- // The reverse-argument membership predicate is defined as the lowest-precedence // operator available. And conveniently, it will not likely collide with anything. template <typename T, typename Container> typename std::enable_if <!std::is_same <Container, T> ::value, bool> ::type operator , ( const T& x, const Container& xs ) { using std::begin; using std::end; return std::find( begin(xs), end(xs), x ) != end(xs); } template <typename T, typename Container> typename std::enable_if <std::is_same <Container, T> ::value, bool> ::type operator , ( const T& x, const Container& y ) { return x == y; } //---------------------------------------------------------------------------- // This thunk is used to accept any type of container without need for // special syntax when used. struct in_container { template <typename Container> const Container& operator = ( const Container& container ) { return container; } template <typename T> std::vector <T> operator = ( std::initializer_list <T> xs ) { return std::vector <T> ( xs ); } }; #endif 

Using

Fine! Now we can use it in every possible way so that you can use the operator in . In accordance with your particular interest, see Example 3:

 #include <iostream> #include <set> #include <string> using namespace std; void f( const string& s, const vector <string> & ss ) { cout << "nope\n\n"; } void f( bool b ) { cout << "fooey!\n\n"; } int main() { cout << "I understand three primes by digit or by name.\n" "Type \"q\" to \"quit\".\n\n"; while (true) { string s; cout << "s? "; getline( cin, s ); // Example 1: arrays const char* quits[] = { "quit", "q" }; if (s in quits) break; // Example 2: vectors vector <string> digits { "2", "3", "5" }; if (s in digits) { cout << "a prime digit\n\n"; continue; } // Example 3: literals if (s in {"two", "three", "five"}) { cout << "a prime name!\n\n"; continue; } // Example 4: sets set <const char*> favorites{ "7", "seven" }; if (s in favorites) { cout << "a favorite prime!\n\n"; continue; } // Example 5: sets, part deux if (s in set <string> { "TWO", "THREE", "FIVE", "SEVEN" }) { cout << "(ouch! don't shout!)\n\n"; continue; } // Example 6: operator weirdness if (s[0] in string("014") + "689") { cout << "not prime\n\n"; continue; } // Example 7: argument lists unaffected f( s, digits ); } cout << "bye\n"; } 

Potential improvements

There are always things you can do to improve the code for your specific goals. You can add the ni (not-in) operator (add a new thunk container type). You can wrap thunk containers in a namespace (good idea). You can specialize in things like std::set to use the .count() member function instead of looking for O (n). Etc.

Other problems

  • const vs mutable : not a problem; both can be used with an operator
  • laziness or : Technically, or not lazy, it is shorted. The std::find() algorithm also closes the same way.
  • reversal of the compilation cycle: not applicable here. No loops were used in the source code; while std::find() does, any loop unfolding that might happen depends on the compiler.
  • it’s easy to extend to operators other than == : this is really a separate issue; You no longer look at a simple membership predicate, but now consider a functional reset filter. It is possible to create an algorithm that does this, but the standard library provides any_of() , which does just that. (It's just not as pretty as our RLM “in” statement. However, any C ++ programmer will easily understand this. Such answers have already been proposed here.)

Hope this helps.

+10
Mar 15 '16 at 19:45
source share

First, I recommend using a for loop, which is the simplest and most readable solution:

 for (i = 0; i < n; i++) { if (var == eq[i]) { // if true break; } } 

However, other methods are available, for example, std::all_of , std::any_of , std::none_of (in #include <algorithm> ).

Let's look at a simple sample program containing all of the above keywords

 #include <vector> #include <numeric> #include <algorithm> #include <iterator> #include <iostream> #include <functional> int main() { std::vector<int> v(10, 2); std::partial_sum(v.cbegin(), v.cend(), v.begin()); std::cout << "Among the numbers: "; std::copy(v.cbegin(), v.cend(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\\n'; if (std::all_of(v.cbegin(), v.cend(), [](int i){ return i % 2 == 0; })) { std::cout << "All numbers are even\\n"; } if (std::none_of(v.cbegin(), v.cend(), std::bind(std::modulus<int>(), std::placeholders::_1, 2))) { std::cout << "None of them are odd\\n"; } struct DivisibleBy { const int d; DivisibleBy(int n) : d(n) {} bool operator()(int n) const { return n % d == 0; } }; if (std::any_of(v.cbegin(), v.cend(), DivisibleBy(7))) { std::cout << "At least one number is divisible by 7\\n"; } } 
+8
Mar 14 '16 at 9:59
source share

You can use std :: set to check if var belongs to it. (Compile with C ++ 11 enabled)

 #include <iostream> #include <set> int main() { std::string el = "abc"; if (std::set<std::string>({"abc", "def", "ghi"}).count(el)) std::cout << "abc belongs to {\"abc\", \"def\", \"ghi\"}" << std::endl; return 0; } 

The advantage is that std::set<std::string>::count works in O(log(n)) time (where n is the number of lines to test) compared to non-compact if witch O(n) in general . The disadvantage is that building a set takes O(n*log(n)) . So, build it once, for example:

 static std::set<std::string> the_set = {"abc", "def", "ghi"}; 

But, IMO, it would be better to leave the condition as is, if it contains more than 10 lines for verification. Performance benefits when using std :: set for such a test are displayed only for large n . Also, a simple non-compact if easier to read for the average C ++ developer.

+6
Mar 14 '16 at 11:10
source share

The closest thing would be something like this:

 template <class K, class U, class = decltype(std::declval<K>() == std::declval<U>())> bool in(K&& key, std::initializer_list<U> vals) { return std::find(vals.begin(), vals.end(), key) != vals.end(); } 

We need to take an argument of type initializer_list<U> so that we can go to a list with an extended set of commands, for example {a,b,c} . This copies the elements, but apparently we do it because we provide literals, so this is probably not a big problem.

We can use it like this:

 std::string var = "hi"; bool b = in(var, {"abc", "def", "ghi", "hi"}); std::cout << b << std::endl; // true 
+4
Mar 14 '16 at 17:48
source share

If you have access to C ++ 14 (not sure if this works with C ++ 11), you can write something like this:

 template <typename T, typename L = std::initializer_list<T>> constexpr bool is_one_of(const T& value, const L& list) { return std::any_of(std::begin(list), std::end(list), [&value](const T& element) { return element == value; }); }; 

The call will look like this:

 std::string test_case = ...; if (is_one_of<std::string>(test_case, { "first case", "second case", "third case" })) {...} 

or how is it

 std::string test_case = ...; std::vector<std::string> allowedCases{ "first case", "second case", "third case" }; if (is_one_of<std::string>(test_case, allowedCases)) {...} 



If you don't like to "wrap" resolved cases into a list type, you can also write a small helper function as follows:

 template <typename T, typename...L> constexpr bool is_one_of(const T& value, const T& first, const L&... next) //First is used to be distinct { return is_one_of(value, std::initializer_list<T>{first, next...}); }; 

This will allow you to call it as follows:

 std::string test_case = ...; if (is_one_of<std::string>(test_case, "first case", "second case", "third case" )) {...} 

Full example at Coliru

+3
Mar 14 '16 at 11:30
source share

It is worth noting that in most Java and C ++ codes that I have seen, listing 3 or so conventions is an accepted practice. This is certainly more readable than smart solutions. If this happens so often, it’s a serious resistance that the smell of design in any case and the template or polymorphic approach will probably help to avoid this.

So my answer is a “zero” operation. Just keep doing the more verbose thing, it is most acceptable.

+2
Mar 14 '16 at 18:09
source share

You can use the switch enclosure. Instead of listing individual cases, you can:

turn on

using the std namespace;

int main () {char grade = 'B';

 switch(grade) { case 'A' : case 'B' : case 'C' : cout << "Well done" << endl; break; case 'D' : cout << "You passed" << endl; break; case 'F' : cout << "Better try again" << endl; break; default : cout << "Invalid grade" << endl; } cout << "Your grade is " << grade << endl; return 0; 

}

So, you can group the results together: A, B and C will output "well done." I took this example from the Tutorials Point: http://www.tutorialspoint.com/cplusplus/cpp_switch_statement.htm

0
Apr 05 '16 at 10:08 on
source share



All Articles