Enough to say "foo not in {bar, baz}" in C ++ 11/14

I am writing C ++ and skipping Python clarity. But I know that C ++ is evolving and wondering if there is a better way to do something like this:

if (foo != bar && foo != baz) 

In Python, I would do the following:

 if foo not in {bar, baz}: 

Is there a fancy feature in C ++ 11 or C ++ 14 that allows me to do something similar for reading?

Edit: Many people wonder why I'm trying to replace something so short. I didn’t, but I didn’t want my example to be as ugly and unreadable as the source code. This is more like:

 if (somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::A_LONG_CONSTANT_NAME && somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME) { // foo 
+5
source share
8 answers

How about something like this:

 #include <type_traits> #include <tuple> #include <utility> template <typename ...Args> struct InT: std::tuple<Args...> { template <typename ...Brgs> explicit InT(Brgs &&... brgs) : std::tuple<Args...>(std::forward<Brgs>(brgs)...) {} template <typename T, std::size_t ...I> bool noteq(T && t, std::index_sequence<I...>) const { return (true && ... && (t != std::get<I>(*this))); } }; template <typename ...Args> InT<Args &&...> AnyOf(Args &&... args) { return InT<Args &&...>(std::forward<Args>(args)...); } template <typename T, typename ...Args> bool operator!=(T && t, InT<Args...> in) { return in.noteq(std::forward<T>(t), std::index_sequence_for<Args...>()); } 

Using:

 if (x != AnyOf(1, 3, 5)) { f(); } 
+10
source

We can get this syntax:

 int main() { if (foo *in* std::tie(bar, baz)) { } } 

living example .

It also works with C arrays or std containers or the like. on the right side *in* .

This should be zero overhead after the optimizer gets his teeth into it.

Denial is simple:

  if (!(foo *in* std::tie(bar, baz))) 

since I don’t think a special case is a good plan. If you want the syntax foo *not in* std::tie(bar, baz)) , see the bottom of this post.


Firstly, the named operator library:

 namespace named_operator { template<class D>struct make_operator{constexpr make_operator(){}}; template<class T, char, class O> struct half_apply { T&& lhs; }; template<class Lhs, class Op> half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) { return {std::forward<Lhs>(lhs)}; } template<class Lhs, class Op, class Rhs> auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs ) -> decltype( invoke( std::declval<Lhs>(), Op{}, std::declval<Rhs>() ) ) { return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ); } } 

length of about 12 lines and simplifies the work with names.

Now we create a named statement. namespace my_ns {constexpr struct in_tag: named_operator :: make_operator {} in {}; } using my_ns :: in; He needs action. C ++ 17 version is simple:

 namespace my_ns { // foo in tuple support: template<class T, class...Args> bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs ) { return std::apply( [&](auto&&...args){ return (false || ... || (lhs == args)); }, rhs); } // foo in container support: template<class T, class Container> bool invoke( T const& lhs, in_tag, Container const& rhs ) { using std::begin; using std::end; auto it = std::find( begin(rhs), end(rhs), lhs ); return it != end(rhs); } } 

The C ++ 11 tuple support version is a bit more complicated due to the lack of std::apply and bending extension:

 namespace my_ns { // tuple support: template<class T, class...Args, std::size_t...Is> bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs, std::index_sequence<Is...> ) { bool retval = false; using discard=int[]; (void)discard{ 0,(void( retval = retval || (lhs == std::get<Is>(rhs)) ),0)... }; return retval; } template<class T, class...Args> bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs ) { return invoke(lhs, in_tag{}, rhs, std::index_sequence_for<Args...>{} ); } // container support is identical to C++17 version } 

As mentioned above if you want

  if (foo *not in* std::tie(bar, baz)) 

I can do it.

Add a not_in :

 namespace my_ns { constexpr struct not_in_tag:named_operator::make_operator<not_in_tag> {} not_in {}; } using my_ns::not_in; 

Then we determine ! which switches between them:

 namespace my_ns { constexpr not_in_tag operator!(in_tag){return {};} constexpr in_tag operator!(not_in_tag){return {};} | 

and what the not_in operator not_in :

 namespace my_ns { template<class T, class Rhs> bool invoke( T const& lhs, not_in_tag, Rhs const& rhs ) { return !invoke(lhs, in_tag{}, rhs ); } } 

for invoke .

Now we get

  if (foo *not in* std::tie(bar, baz)) { std::cout << "foo not in {bar,baz}\n"; } if (foo *not in* std::make_tuple(bar, baz, 3)) { std::cout << "foo not in {bar,baz, 3}\n"; } 

or

  if (foo *not_in* std::tie(bar, baz)) { std::cout << "foo not in {bar,baz}\n"; } 

or

  if (foo *!in* std::tie(bar, baz)) { std::cout << "foo not in {bar,baz}\n"; } 

depending on what you want.

living example

+4
source

I highly recommend that you don't contaminate your code with fancy stuff if necessary.

The following C ++ 14 solution offers infix syntax, as in Python, if you have more than two values ​​to compare:

 #include <tuple> #include <utility> template <typename... T> struct in_checker : std::tuple<T...> { using std::tuple<T...>::tuple; template <typename U, std::size_t... Is> constexpr bool contains(U const& u, std::index_sequence<Is...>) const { for (auto b : {std::get<Is>(*this) == u...}) if (b) return true; return false; } template <typename U> constexpr bool contains(U const& u) const { return contains(u, std::index_sequence_for<T...>{});} }; template <typename U, typename... T> constexpr bool operator==(U const& u, in_checker<T...> const& in) { return in.contains(u);} template <typename U, typename... T> constexpr bool operator!=(U const& u, in_checker<T...> const& in) { return !(u == in);} template <typename... T> constexpr in_checker<T...> in(T const&... t) {return std::tie(t...);} #include <iostream> int main() { int t = 2; if (t == in(1, 2, 3)) std::cout << "Congrats"; if (t != in(1, 3, 4)) std::cout << "... again!"; } 

Demo

+3
source

Not very elegant, I suppose, but you can write a simple isIn() template function

 template <typename T> bool isIn (const T & val, const std::set<T> & s) { return s.cend() != s.find(val); } 

and the following simple usage example when T is int

 #include <set> #include <iostream> template <typename T> bool isIn (const T & val, const std::set<T> & s) { return s.cend() != s.find(val); } int main () { int bar = 5; int baz = 3; int foo = 0; if ( false == isIn(foo, {bar, baz}) ) std::cout << foo << " isn\'t in set" << std::endl; else std::cout << foo << " is in set" << std::endl; foo = 3; if ( false == isIn(foo, {bar, baz}) ) std::cout << foo << " isn\'t in set" << std::endl; else std::cout << foo << " is in set" << std::endl; return 0; } 

--- Edit post -

@guidoism: I think your question is interesting in a more general way, but if you should only check somelongmethodname() for enum values, I think the readable solution might be the good old switch

  using SomeReallyLongNamespace::AndAnotherSubClassname; switch ( somelongvariablename.somelongmethodname() ) { case A_LONG_CONSTANT_NAME: case ANOTHER_LONG_CONSTANT_NAME: // do nothing (or something, if you like) break; default: // do something break; } 
+2
source

I like the elegant simplicity of Python syntax:

 if foo not in {bar, baz} 

In a scripting language, minimalistic code is the highest level. But under the hood, this line is about to:

  • Create a simple container like C ++ initializer_list
  • Insert links in bar and baz in container
  • Get a link to 1 st element in the container
  • Compare this link with foo if you destroy the container and go to the false branch.
  • Get the following link in the container
  • Compare this link with foo if you destroy the container and go to the false branch.
  • Destroy the container and insert it into the true branch

In C ++, the highest quality is speed, so let's see how we are forced to do this in C ++:

 if(foo != bar && foo != baz) 
  • Compare foo to bar if equal branch to false
  • Compare foo with baz if equal branch to false
  • Go to true branch

This is not to say that a good compiler could not optimize the container in Python if -statement, but if these variables represent objects with different constructive rules, which may be almost impossible. If C ++ suits us with this syntax, I will be the second to accept it right after you. But at the moment, building a temporary container to hide individual comparisons is a bad decision for two reasons:

1. Unexpected container construction costs cannot be optimized

2. Since there are no standard keywords, the cost for the reader to figure out what is happening outweighs the elegance gained by using the container

So for now, the best solution is still reliable: if(foo != bar && foo != baz)


This does not say that we cannot use the container in C ++, although given the type foo , bar and baz is int , you can do any of these great evils:

  • if(basic_string<int>{bar, baz}.find(foo) == -1)
  • if(set<int>{bar, baz}.insert(foo).second)
  • if(!set<int>{bar, baz}.count(foo))

EDIT:

After viewing your edit, your question should be said that even if you can use Python syntax that only saves you: characters-in- foo + 4 characters. Based on the code:

 if (somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::A_LONG_CONSTANT_NAME && somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME) 

If you say that you have public , static variables like this:

 namespace SomeReallyLongNamespace { struct AndAnotherSubClassname{ static const auto A_LONG_CONSTANT_NAME = 13; static const auto ANOTHER_LONG_CONSTANT_NAME = 42; }; } 

Then a using -statement will eliminate a ton of input, not only here, but everywhere within the scope in which using is defined:

 using namespace SomeReallyLongNamespace; if (somelongvariablename.somelongmethodname() != AndAnotherSubClassname::A_LONG_CONSTANT_NAME && somelongvariablename.somelongmethodname() != AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME) 

Further, assuming that somelongvariablename.somelongmethodname() is a const method, it is best practice to return it in constant time, so you only need to call the method once, improving our code again:

 using SomeReallyLongNamespace::AndAnotherSubClassname; const auto& foo = somelongvariablename.somelongmethodname(); if(foo != AndAnotherSubClassname::A_LONG_CONSTANT_NAME && foo != AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME) 

There are obviously a couple of conditions there, but I would say that if you can do this, your problem will significantly improve your code to such an extent that you only save 7 characters with Python syntax, returning the old faithful C ++ simulator for a viable rival .

+1
source
 #include <iostream> #include <initializer_list> using namespace std; template<typename T> initializer_list<T> in(initializer_list<T> K) { return K; } template<typename T> bool operator !=(TA, initializer_list<T> B) { bool R = true; for (auto E : B)R = R && (A != E); return R; } int main() { if (1 != in({2,3,4})) { cout << "Wow" << endl; } return 0; } 

this way we can make the code more readable like 1 != in({2,3,4})

EDIT:

Found a more readable way.

 #include <iostream> #include <initializer_list> using namespace std; template<typename T> initializer_list<T> o(initializer_list<T> K) { return K; } class not_i { }; class not_i_helper1 { public: int S; }; not_i_helper1 operator<(int A, not_i P) { not_i_helper1 K; KS = A; return K; } bool operator>(not_i_helper1 S, initializer_list<int> B) { bool R = true; for (auto E : B)R = R && (SS != E); return R; } not_i not_in; int main() { if (1 < not_in > o({ 2,3,4 })) { cout << "Success!" << endl; } else { cout << "Huh?" << endl; } return 0; } 

now we can use 1 < not_in > o({ 2,3,4 })

+1
source

If your business is really simple, you can simply do this:

 for (auto& e : {bar, baz}) if (foo == e) { /* found */ }; 

Notice how it is, it has no way of telling if there is.

0
source

This is one liner on my 15 "16: 9: D laptop

 #include <iostream> #include <set> int main() { for(std::set<int> omg({1, 2, 3, 4}); omg.find(42) == omg.cend(); ) { std::cout << "There no Answer." << std::endl; break; } } 
-1
source

All Articles