I'm writing C++ and missing the clarity of Python. But I know that C++ has been evolving and wondering if there's a nicer way to do something like this:
if (foo != bar && foo != baz)
In Python I would do this:
if foo not in {bar, baz}:
Is there a fancy feature in C++11 or C++14 that allows me to do something similarly readable?
Edit: A lot of people are wondering why I'm trying to replace something so short. I'm not, but I didn't want to make my example as ugly and unreadable as the original code. It's more like:
if (somelongvariablename.somelongmethodname() !=
SomeReallyLongNamespace::AndAnotherSubClassname::A_LONG_CONSTANT_NAME &&
somelongvariablename.somelongmethodname() !=
SomeReallyLongNamespace::AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME) {
// foo
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...>());
}
Usage:
if (x != AnyOf(1, 3, 5)) { f(); }
We can get this syntax:
int main() {
if (foo *in* std::tie(bar, baz)) {
}
}
live example.
It also works with C arrays or std containers or the like on the right hand side of *in*
.
This should be zero overhead after the optimizer gets its teeth into it.
Negating is just:
if (!(foo *in* std::tie(bar, baz)))
as I don't think a special case is a good plan. If you want the foo *not in* std::tie(bar, baz))
syntax, see bottom of this post.
First, a 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) );
}
}
which is about 12 lines long, and makes named operators easy.
Now we make a named operator. namespace my_ns { constexpr struct in_tag:named_operator::make_operator {} in {}; } using my_ns::in; It needs an action. The C++17 version is easy:
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 trickier, because of the lack of std::apply
and fold expansion:
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
operator:
namespace my_ns {
constexpr struct not_in_tag:named_operator::make_operator<not_in_tag> {} not_in {};
}
using my_ns::not_in;
We then define !
that toggles 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 does:
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";
}
whichever you want.
live example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With