template<class Lower>
auto is_gt(Lower&&l){
return [=](auto&&x){ return l<x; };
}
template<class Higher>
auto is_lt(Higher&&l){
return [=](auto&&x){ return x<l; };
}
template<class L, class R>
auto also(L&& l, R&& r){
return [=](auto&&x){return l(x)&&r(x); };
}
template<class L, class R>
auto either(L&& l, R&& r){
return [=](auto&&x){return l(x)||r(x); };
}
template<class Lower, class Upper>
auto between(Lower&& l, Upper&& u){
return also(is_gt(l), is_lt(u));
}
are some toys.
Then:
if (either(between(90,97), is_gt(122))(ncode[i]+key[ikey])) {
This can be made a bit more slick. If we define a predicate to be a function object returning bool
that supports &&
and ||
and ~
overloading, and have our above toys return predicates, we get:
if (( between(90,97) || is_gt(122))(ncode[i]+key[ikey])) {
getting rid of the either
prefix noise.
For more efficiency in non-int
cases, capture the bounds by move.
template<class F>
struct predicate_t:F{
predicate_t(F const& f):F(f){}
template<class F2>
friend auto operator&&(predicate const& f1,predicate<F2> const& f2){
auto r=[=](auto&&...args){
return f1(args...)&&f2(args...);
};
return predicate_t<decltype(r)>(r);
}
template<class F2>
friend auto operator||(predicate const& f1,predicate<F2> const& f2){
auto r=[=](auto&&...args){
return f1(args...)||f2(args...);
};
return predicate_t<decltype(r)>(r);
}
template<class F2>
friend auto operator!(predicate const& f1){
auto r=[=](auto&&...args){
return !f1(args...);
};
return predicate_t<decltype(r)>(r);
}
};
template<class F>
predicate_t<std::decay_t<F>>
predicate(F&&f){
return std::forward<F>(f);
}
With each of the above toy
functions returning return predicate(lambda);
instead of lambda.
This code is simpler and shorree at the point of use, should have zero run time overhead, but is a different style of coding than conventional C++.