How to create a generic function for combining binary predicates? - templates

There are several related questions, e.g. algorithms with multiple (unary) predicates, or multiple predicates in general. But they do not seem to tackle the generic case that I'm aiming at, and the answers are often out-dated in that they refer to constructs that are deprecated in C++11. It might all boil down to this question related to type deduction in lambdas, but I'm not sure whether it can be considered as a duplicate.
I tried to create a function that combines two arbitrary binary predicates (in the meaning of "types that implement a two-argument operator() and return a bool"). My goal was to have the possibility to write code like this:
auto p0 = somePredicate();
auto p1 = someOtherPredicate();
auto p2 = evenMorePredicates();
auto combined = and(p0, or(p1, p2));
I know that something similar can be achieved with lambdas (as also suggested in the answer to one of the questions linked above), but this requires the argument types to be repeated in the lambda itself. I'd like to know how such an and or or function could really be implemented generically - particularly, for binary predicates with arbitrary argument types.
My basic approach (and a suggestive example) is the following:
#include <functional>
#include <iostream>
template <typename A, typename B, typename P0, typename P1>
std::function<bool(const A&, const B&)> and(
const P0& p0, const P1& p1)
{
return [p0, p1](const A& a, const B& b)
{
return p0(a, b) && p1(a, b);
};
}
int main(int argc, char* argv[])
{
auto p0 = [](const int& a, const int& b)
{
return a < 0 && b > 0;
};
auto p1 = [](const int& a, const int& b)
{
return a < 1 && b > 4;
};
// This is what I want:
//auto combined = and(p0, p1);
// This works, but ...
auto combined = and<int, int, std::function<bool(const int&, const int&)>, std::function<bool(const int&, const int&)>>(p0, p1);
std::cout << "combined(-3,7) : " << combined(-3, 7) << std::endl;
std::cout << "combined(-3,1) : " << combined(-3, 1) << std::endl;
return 0;
}
In any case, the problem seems to be that the template parameters of the argument types can not be deduced at the call site. I tried variations thereof, based on other related questions here on stackoverflow, but no avail. My gut feeling is that there must be a (simple) solution for this, sneaking around some limitation of the type deduction system of lambdas, but I obviously didn't find the right path yet.

You could write a functor class to carry your expression:
template <typename P0, typename P1>
class AndFunctor {
public:
AndFunctor (P0 p0, P1 p1)
: m_p0{std::move(p0)}, m_p1{p1}
{}
template <typename T, typename U>
bool operator() (T&& t, U&& u) {
return m_p0(t, u) && m_p1(t, u);
}
private:
P0 m_p0;
P1 m_p1;
};
Then return an instance of this in your and function (and is a keyword, so this code renames it to and_p
template <typename P0, typename P1>
AndFunctor<P0,P1> and_p(P0 p0, P1 p1)
{
return { std::move(p0), std::move(p1) };
}
Then you just use it as you envisaged:
auto combined = and_p(p0, p1);
Live Demo

Related

C++ passing by const ref vs universal ref

So I've recently learned about universal references and reference collapsing.
So let's say I have two different implementations of a max function like such.
template<class T>
T&& max(T&& a, T&& b)
{
return (a < b) ? b : a;
}
template<class T>
const T& max(const T& a, const T& b)
{
return (a < b) ? b : a;
}
One version takes its arguments by const reference and other takes them by universal reference.
What confuses me about these is when should either be used. Passing by const is mainly used so you can bind temporaries to it.
In the stl there's different uses as well. std::move takes universal reference where std::max takes by const ref.
What's the situations on where either should be used??
Say if I wanted to avoid the copy when it returns or keep a reference on return. Would it makes sense to have a const and nonconst version of the function etc
The first one should probably be:
// amended first version
template<class T>
decltype(auto) max(T&& a, T&& b) {
return (a < b) ? std::forward<T>(b) : std::forward<T>(a);
}
Otherwise it wouldn't work for temporaries (the original first option fails with two temporaries).
But even now, with the new version above, it cannot work for a mix of an rvalue and an lvalue:
int i = my_max(3, 5); // ok
i = my_max(i, 15); // fails
// no known conversion from 'int' to 'int &&' for 1st argument
To add to that, the return value of the first option is either an lvalue-ref or an rvalue-ref (with or without const, depending on the arguments). Getting back rvalue-ref to a temporary would not be the best idea, it would work if we immediately copy from it, but then it would be better to return byvalue, which is not the approach taken by the first version.
The pitfall with the second, the const-lvalue-ref version, is that if a temporary is sent to it, we return a const-lvalue-ref to a temporary which is bug prone (well, not more than returning an rvalue-ref to a temporary, but still). If you copy it immediately you are fine, if you take it by-ref you are in the UB zone:
// second version
template<class T>
const T& max(const T& a, const T& b) {
return (a < b) ? b : a;
}
std::string themax1 = max("hello"s, "world"s); // ok
const std::string& themax2 = max("hello"s, "world"s); // dangling ref
To solve the above problem, with the cost of redundant copying for the lvalue-ref case, we can have another option, returning byvalue:
// third version
template<class T1, class T2>
auto max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
The language itself took the 2nd option for std::max, i.e. getting and returning const-ref. And the user shall be careful enough not to take a reference to temporaries.
Another option might be to support both rvalue and lvalue in their own semantic, with two overloaded functions:
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
template<class T>
const T& my_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
With this approach, you can always get the result as a const-ref: if you went to the first one you get back a value and extend its lifetime, if you went to the second one you bind a const-ref to a const-ref:
int i = my_max(3, 5); // first, copying
const int& i2 = my_max(i, 25); // first, life time is extended
const std::string& s = my_max("hi"s, "hello"s); // first, life time is extended
const std::string s2 = my_max("hi"s, "hello"s); // first, copying
const std::string& s3 = my_max(s, s2); // second, actual ref, no life time extension
The problem with the above suggestion is that it only takes you to the lvalue-ref version if both arguments are const lvalue-ref. If you want to cover all cases you will have to actually cover them all, as in the code below:
// handle rvalue-refs
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
// handle all lvalue-ref combinations
template<class T>
const T& my_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(T& a, const T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(const T& a, T& b) {
return (a < b) ? b : a;
}
template<class T>
const T& my_max(T& a, T& b) {
return (a < b) ? b : a;
}
A last approach to achieve the overloading, and supporting all kind of lvalue-ref (const and non-const, including a mixture), without the need for implementing the 4 combinations for lvalue-ref, would be based on SFINAE (here presented with C++20 with a constraint, using requires):
template<class T1, class T2>
auto my_max(T1&& a, T2&& b) {
return (a < b) ? std::forward<T2>(b) : std::forward<T1>(a);
}
template<class T1, class T2>
requires std::is_lvalue_reference_v<T1> &&
std::is_lvalue_reference_v<T2> &&
std::is_same_v<std::remove_cvref_t<T1>, std::remove_cvref_t<T2>>
auto& my_max(T1&& a, T2&& b) {
return (a < b) ? b : a;
}
And if you bear with me for one past the last... (hope you are not in a bucket for monsieur state by now). We can achieve it all in one function!, and as a side benefit, even allow cases of comparison between derived and base if supported, so the following would work fine:
A a = 1;
B b = 2; // B is derived from A
// if comparison between A and B is supported, you can do
const A& max1 = my_max(a, b); // const-ref to b
const A& max2 = my_max(a, B{-1}); // const-ref to a temporary copied from a
const A& max3 = my_max(a, B{3}); // const-ref to B{3}
This would be the code to support this with a single function:
template<typename T1, typename T2>
struct common_return {
using type = std::common_reference_t<T1, T2>;
};
template<typename T1, typename T2>
requires std::is_lvalue_reference_v<T1> &&
std::is_lvalue_reference_v<T2> &&
has_common_base<T1, T2> // see code in link below
struct common_return<T1, T2> {
using type = const std::common_reference_t<T1, T2>&;
};
template<typename T1, typename T2>
using common_return_t = typename common_return<T1, T2>::type;
template<class T1, class T2>
common_return_t<T1, T2> my_max(T1&& a, T2&& b)
{
if(a < b) {
return std::forward<T2>(b);
}
return std::forward<T1>(a);
}
The machinery for the above can be found here.
For the variadic version of this, you can follow this SO post.

How to get class type from member pointer template type

For sorting user defined typed objects in a flexible way (i.e. by naming a member variable) I wrote a template to generate lambdas to do the comparison. Additionally to chain comparisons of different member variables in case of equality I wrote a second template. It works so far but I want bpth templates to be completely independent from any concrete types. Therefore I have to get a class type from a class member pointer type.
This is my user defined example type:
struct Person { string name; int age, height; };
To sort objects of it by looking at e.g. the age I want to write it like:
auto result = max_element(persons.begin(), persons.end(), order_by(&Person::age));
This works with the template:
template<class F> //F is Person::* e.g. &Person::age
auto order_by(F f) {
return [f](const Person& smaller, const Person& bigger) {
return smaller.*f < bigger.*f;
};
}
To be able to chain multiple comparisons in case of equal values like this:
result = max_element(persons.begin(), persons.end(), order_by(&Person::age) | order_by(&Person::height));
I wrote the template:
//compose two orderings :
template<class F1, class F2>
auto operator|(F1 f1, F2 f2) {
return [f1, f2](auto a, auto b) {
auto res = f1(a, b);
auto inv_res = f1(b, a);
if (res != inv_res)
return res;
return f2(a, b);
};
}
Here the first comparison is done and if it detects that a==b (a is not smaller than b and b is not smaller than a) it uses the second comparison function.
What I want to achieve is to be independent of the Person type in the first template. How could this be solved?
You can easily extract the class and type of the pointer-to-member in your first template with some small modifications.
template<class Class, class Type>
auto order_by(Type Class::* f) {
return [f](const Class& smaller, const Class& bigger) {
return smaller.*f < bigger.*f;
};
}
I would forward job to std::tuple with something like:
template <typename... Projs>
auto order_by(Projs... projs) {
return [=](const auto& lhs, const auto& rhs) {
return std::forward_as_tuple(std::invoke(projs, lhs)...)
< std::forward_as_tuple(std::invoke(projs, rhs)...);
};
}
with usage
result = std::max_element(persons.begin(), persons.end(), order_by(&Person::age, &Person::height));
ranges algorithms (C++20 or range-v3) separate comparison from projection, so you might have (by changing order_by to project_to):
result = ranges::max_element(persons, std::less<>{}, project_to(&Person::age, &Person::height));
You can get the class type from the type of a pointer to member like this:
#include <type_traits>
#include <iostream>
struct Foo {
int bar;
};
template <typename T>
struct type_from_member;
template <typename M,typename T>
struct type_from_member< M T::* > {
using type = T;
};
int main()
{
std::cout << std::is_same< type_from_member<decltype(&Foo::bar)>::type, Foo>::value;
}
Output:
1
Because type_from_member< decltype(&Foo::bar)>::type is Foo.
So you could use it like this:
template<class F> //F is Person::* e.g. &Person::age
auto order_by(F f) {
using T = typename type_from_member<F>::type;
return [f](const T& smaller, const T& bigger) {
return smaller.*f < bigger.*f;
};
}

std::function with templated arguments

I want to write a templated function that applies some function over pairs of elements coming from two vectors. The result should be a new vector of results. I want this to be a templated function so that it works on different types.
I tried the definition before. However, when I try to apply it to some particular function, I get a compilation error.
#include <vector>
#include <cmath>
#include <iostream>
#include <functional>
using namespace std;
template<typename T1, typename T2, typename T3>
vector<T3> mapzip2(const vector<T1> &xs, const vector<T2> &ys, std::function<T3(T1, T2)> l) {
if (xs.size() != ys.size())
throw runtime_error("mapzip2: container sizes (" + to_string(xs.size()) +
" and " + to_string(ys.size()) + ") do not match");
vector<T3> result;
result.reserve(xs.size());
for (int i = 0; i < xs.size(); i++)
result.push_back(l(xs[i], ys[i]));
return result;
}
constexpr double PRECISION = 1E-6;
bool feq(double a, double b) {
return abs(a - b) < PRECISION;
}
int main() {
vector<double> a = {0.3, 0.42, 0.0, -7.34};
vector<double> b = {0.3, 0.42, 0.0, -7.34};
// compilation error: no matching function for call to
// ‘mapzip2(std::vector<double>&, std::vector<double>&, bool (&)(double, double))’
vector<bool> result = mapzip2(a, b, feq);
for (bool b: result) cout << b << ' ';
cout << endl;
}
What is wrong here with types deduction?
You have a sort of chicken-and-egg problem.
The T3 type in
template<typename T1, typename T2, typename T3>
T3 mapzip2(const vector<T1> &xs, const vector<T2> &ys, std::function<T3(T1, T2)> l)
has to be deduced from the third argument, a std::function<T3(T1, T2)>
But when you call
bool feq(double a, double b) {
return abs(a - b) < PRECISION;
}
// ...
vector<bool> result = mapzip2(a, b, feq);
you call mapzip() with feq that can be converted to a std::function<bool(double, double)> but isn't a std::function<bool(double, double)>
So the T3 type can't be deduced as bool because to convert feq to std::function<bool(double, double)> you have to know, before the deduction, that T3 is bool.
Possible solutions:
(1) explicit the template types calling mapzip()
vector<bool> result = mapzip2<double, double, bool>(a, b, feq);
This way the compiler know that T3 is bool, so convert feq to std::function<bool(double, double)>
(2) construct a std::function<bool(double, double)> with feq
vector<bool> result = mapzip2(a, b, std::function<double, double, bool>{feq});
so the compiler can receive a std::function as third argument and deduce T3 from it.
(3) (more flexible and, IMHO, the best of the three) avoid std::function and use a more generic functional typename for the function
template<typename T1, typename T2, typename F>
auto mapzip2(const vector<T1> &xs, const vector<T2> &ys, F l) {
if (xs.size() != ys.size())
throw runtime_error("mapzip2: container sizes (" + to_string(xs.size()) +
" and " + to_string(ys.size()) + ") do not match");
vector<decltype(l(xs[0], ys[0]))> result; // <-- use decltype() !
result.reserve(xs.size());
for (int i = 0; i < xs.size(); i++)
result.push_back(l(xs[i], ys[i]));
return result;
}
Observe the use of decltype() to deduce the type of the returned vector (the old T3) and the use of auto (starting from C++14) for the returned type of the function.
If you can't use C++14 (only C++11), you have to add the trailing return type
template<typename T1, typename T2, typename F>
auto mapzip2(const vector<T1> &xs, const vector<T2> &ys, F l)
-> std::vector<decltype(l(xs[0], ys[0]))>
{
}
Observe also -- as pointed by ypnos in a comment -- that the signature of your original mapzip2() is wrong: you return result, a std::vector<T3>, not a T3.
The problem is that template functions don't infer types and don't do implicit casting (if you don't supply the types, just let the compiler generate the function). The compiler just tries to find a simple match. Consider this example:
template<typename T>
T add2(T a, T b)
{
T res = a + b;
return res;
}
int main()
{
int a = add2(10, 20); // ok
double b = add2(10.2, 20); // error, no implicit cast from int to double
return 0;
}
The second assignment in main will emit no matching function for call to ‘add2(double, int)’ error.
As in your case, you pass feq which is of type bool (*)(double, double), that is, it's a function pointer, while mapzip2 expects std::function object. There is no implicit casting for templates.
As others suggested, you can build the function object explicitly.
(also as others noted, you need to return vector<T3>, not just T3, but this is a second problem, not related to the original one).
Finally, if you do supply the template types, the compiler will indeed try implicit casting, for example, in the above example, the following will work:
double b = add2<double>(10.2, 20);
The Standard Library solves this issue by using iterators. It would be a good idea to use them too since your code has the same structure as a standard algorithm:
// Overload #1
template<class I1, class I2, class O, class F>
void zip(I1 begin1, I1 end1, I2 begin2, O out_it, F f) {
while (begin1 != end1) {
out_it++ = f(*begin1++, *begin2++);
}
}
// Overload #2
template<class C1, class C2, class R, class F>
void zip(C1& c1, C2& c2, R& ret, F f) {
using std::begin; using std::end;
zip(begin(c1), end(c1), begin(c2), std::back_inserter(ret), f);
}
vector<bool> result;
zip(a, b, result, feq);
Or just use std::transform().
If you still want to return the vector from the function, it would help to decouple the return type deduction from the function itself:
template<class T> using value_t = typename std::decay_t<T>::value_type;
template<class F,class... Cs> using zip_ret = std::result_of_t<F&(value_t<Cs>...)>;
template<class C1, class C2, class F, class R=zip_ret<F, C1, C2>>
std::vector<R> zip(C1& c1, C2& c2, F f) {
using std::begin; using std::end;
std::vector<R> ret;
std::transform(begin(c1), end(c1), begin(c2), std::back_inserter(ret), f);
return ret;
}

the following c++ program does not compile and is displaying a variety of errors

So I was reading about auto and other C++11 std feature and made this program:
#include<iostream>
#include<tuple>
using namespace std;
template<typename T1, typename T2>
auto create_pair(const T1 &a, const T2 &b)
{
pair<T1, T2> p(a, b);
return p;
}
int main()
{
auto p1 = create_pair(4, 5.6);
pair<int, float> p(p1);
cout << get<0>(p);
cout << endl;
cout << get<1>(p);
return 0;
}
My question is whether auto can be used in the way prescribed here in the create_pair() function or there exists some other workaround this to achieve the same result.
The error message when compiled with gcc is this:
create_pair function uses auto type specifier without return type.
Automatic return type deduction is a C++14 feature, and your code would work if compiled with C++14. The auto keyword in the return type location is part of the C++11 trailing return type feature, where you specify the return type right before the body with an arrow and the type (e.g., auto foo() -> int {...}).
Without macros, the closest you can get in C++11 is a lambda function with a single statement (the return statement) as its body, and this won't work with templates, and can cause slight issues related to the function being a variable instead of a function.
With macros, you can have something to the effect of auto foo() RETURNS(2), which would expand to auto foo() -> decltype(2) {return 2;}
On a side note, this function exists already; it is std::make_pair, which is implemented a bit more usefully than your version.
The error message fully describes what is going on. In C++11, auto in place of function return type is not return type deduction, but only an alternative way to actually specify the return type, and should be used like this:
template<typename T1, typename T2>
auto create_pair(const T1 &a, const T2 &b) -> std::pair<T1, T2>;
It is intended to be used in contexts like
template <typename T1, typename T2>
auto add (T1 const & x1, T1 const & x2) -> decltype(x1 + x2)
{
return x1 + x2;
}
instead of much uglier
template <typename T1, typename T2>
decltype(std::declval<T1>() + std::declval<T2>()) add (T1 const & x1, T1 const & x2)
{
return x1 + x2;
}
In C++11 return type deduction is available only for lambdas:
auto create_pair = [](int x, int y){ return std::pair<int, int>(x, y); }
Since your function is a template (and generic lambdas allowing to be templated are also C++14 feature), this would not help much.
However, your code should compile under C++14, as the latter brings return type deduction with exactly the same syntax you are using in your code.

Generic equivalent to std function objects

Is there any function objects in the boost that are generic equivalents to the std::equal_to, std::greater etc. family of function objects?
Essentially, std::equal_to should become something like
struct generic_equal_to
{
template <class T, class U>
bool operator()(const T& t, const U& u) const
{
return t == u;
}
};
I can see how the generic versions of std::plus etc. might be trickier due to issues with the return type (though the decltype can solve that). I can't see any possible reason why the std::equal_to function object itself should require a template argument, though.
Surely somewhere in boost or in the STL these versions exist? They are, of course, trivial to write, but I very much dislike duplicating library code, especially for something as apparently trivial as this.
EDIT:
As some context as to why I would want this instead of using lambdas, or another function-object generation method:
I was writing a generic boost::fusion sequence comparison function thusly:
template <class T>
bool sequence_equal(const T& left, const T& right)
{
return fusion::all(
fusion::zip(left, right),
fusion::fused<generic_equal_to>());
}
Note the fusion::fused<generic_equal_to> part, which leads to the isse that you can't practically specify a boost::lambda or boost::phoenix function-object by type. I guess one solution might be decltype:
fusion::fused<decltype(_1 == _2)>()
That seems very awkward though, and might not even work, depending on how boost::lambda or boost::phoenix is implemented - I'm really not sure.
I know you can use fusion::make_fused to get around this whole issue, but then you have to instantiate the function object. The solution I thought of, then, would be a non-template equal_to struct - I called mine generic_equal_to.
I know it's a very trivial problem - after all, make_fused(_1 == _2) will probably inline down to much the same assembly as fused<generic_equal_to>. I just couldn't believe that there was no generic_equal_to function object in boost or in the STL anywhere, hence this question.
I don't think there's anything quite as direct as you're asking for, but there are utilities that not only cover your use-cases, but go beyond. They are Boost.Lambda and Boost.Phoenix (the latter being a more generic successor to the lambda library).
Example using Boost.Lambda for generic equality:
#include <boost/lambda/lambda.hpp>
#include <iomanip>
#include <iostream>
struct foo {};
bool operator==(foo, foo) { return true; }
bool operator==(foo, int) { return false; }
template <typename T, typename U, typename Func>
void f(const T& x, const U& y, Func func)
{
std::cout << func(x, y) << std::endl;
}
int main()
{
using namespace boost::lambda; // for placeholders
std::cout << std::boolalpha;
foo a, b;
int i = 0;
f(a, b, _1 == _2);
f(a, i, _1 == _2);
}
And the same, with Phoenix:
#include <boost/phoenix.hpp>
#include <iomanip>
#include <iostream>
struct foo {};
bool operator==(foo, foo) { return true; }
bool operator==(foo, int) { return false; }
template <typename T, typename U, typename Func>
void f(const T& x, const U& y, Func func)
{
std::cout << func(x, y) << std::endl;
}
int main()
{
using namespace boost::phoenix::arg_names; // for placeholders
std::cout << std::boolalpha;
foo a, b;
int i = 0;
f(a, b, arg1 == arg2);
f(a, i, arg1 == arg2);
}
Each of these can be extended to support the other operators in the obvious way (and more generally, into other expressions). I would personally go with Phoenix, because if you find out you need more functionality than lambda offers you won't end up including both.
Now in C++14 there is std::equal_to<void> (that can be also used as std::equal_to<>)
std::equal_to<> is a specialization of std::equal_to with parameter and return type deduced.
template< class T, class U>
constexpr auto operator()( T&& lhs, U&& rhs ) const
-> decltype(std::forward<T>(lhs) == std::forward<U>(rhs));
Returns the result of equality comparison between lhs and rhs.
Docs