Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Lambda capturing "this" vector comparison function issue

25 views
Skip to first unread message

Aaron Gray

unread,
Oct 4, 2023, 4:24:51 PM10/4/23
to
Having an issue calling 'std::function<bool(const T*, const T*)> comp'
https://godbolt.org/z/7dTj5cKKn

#include <string>
#include <vector>
#include <functional>

class Type {
public:
Type(std::string name) : name(name) {}
std::string name;
};
class Types {
public:
Types(std::initializer_list<const Type*> types) : types(types) {}
Types(std::vector<const Type*> types) : types(types) {}
std::vector<const Type*> types;
};

typedef std::pair<const Type*, const Type*> Pair;

class TypeChecker {
public:

std::unordered_map<Pair, bool> subTypeMemorization;
bool isSubtype(const Type* lhs, const Type* rhs);
bool isSubtype(const Types* lhs, const Types* rhs);
bool isSubtype2(const Types* lhs, const Types* rhs);
};


template <class T>
std::vector<std::pair<const T, const T>> zip(std::vector< const T>* lhs, std::vector<const T>* rhs) {
assert(lhs->size() == rhs->size());
std::vector<std::pair<const T, const T>> result;
for (auto l = lhs->begin(), r = rhs->begin(); l != lhs->end(); ++l, ++r)
result.push_back(std::make_pair(*l, *r));
return result;
}

template <class T>
bool compare(const std::vector<const T*> lhs, const std::vector<const T*> rhs, std::function<bool(const T*, const T*)> comp) {
assert(lhs.size() == rhs.size());
for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
if (comp(*l, *r))
return false;
return true;
}

template <class T>
bool compare(const std::vector<const T*> lhs, const std::vector<const T*> rhs, std::function<bool(TypeChecker*, const T*, const T*)> comp) {
assert(lhs.size() == rhs.size());
for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
if (comp(*l, *r))
return false;
return true;
}

bool TypeChecker::isSubtype(const Types* lhs, const Types* rhs) {
return compare(lhs->types, rhs->types, [this](const Type* l, const Type* r) {
return this->isSubtype(l, r);
});
}

bool TypeChecker::isSubtype2(const Types* lhs, const Types* rhs) {
for (auto l = lhs->types.begin(), r = rhs->types.begin(); l != lhs->types.end(); ++l, ++r)
if (this->isSubtype(*l, *r))
return false;
return true;
}

Paavo Helde

unread,
Oct 4, 2023, 5:44:00 PM10/4/23
to
04.10.2023 23:24 Aaron Gray kirjutas:
> Having an issue calling 'std::function<bool(const T*, const T*)> comp'

[...]

>
> template <class T>
> bool compare(const std::vector<const T*> lhs, const std::vector<const T*> rhs, std::function<bool(const T*, const T*)> comp) {
> assert(lhs.size() == rhs.size());
> for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
> if (comp(*l, *r))
> return false;
> return true;
> }

[...]

> bool TypeChecker::isSubtype(const Types* lhs, const Types* rhs) {
> return compare(lhs->types, rhs->types, [this](const Type* l, const Type* r) {

You do not say what issue you have, but probably one point is that with
the prev line you expect the compiler deduce type T, while no passed
argument has type T and there is a complicated conversion from a lambda
to a std::function. Apparently this confuses the compiler too much, so
it cannot deduce T.

Solution 1: specify the needed type by hand:

return compare<Type>(lhs->types, rhs->types, [this](const Type* l, const
Type* r) {

Solution 2: avoid the lambda conversion by passing it as a template
parameter:

template <class T, class F>
bool compare(const std::vector<const T*> lhs, const std::vector<const
T*> rhs, F comp) {

hth



Aaron Gray

unread,
Oct 4, 2023, 11:38:10 PM10/4/23
to
Hi Paavo,

Okay got solution1 working, many thanks for that, I thought it would be something simple, now I am kicking myself :)

But could not see how to invoke solution 2

On Wednesday, 4 October 2023 at 22:44:00 UTC+1, Paavo Helde wrote:
> 04.10.2023 23:24 Aaron Gray kirjutas:
> > Having an issue calling 'std::function<bool(const T*, const T*)> comp'
> You do not say what issue you have, but probably one point is that with
> the prev line you expect the compiler deduce type T, while no passed
> argument has type T and there is a complicated conversion from a lambda
> to a std::function. Apparently this confuses the compiler too much, so
> it cannot deduce T.
>
> Solution 1: specify the needed type by hand:
>
> return compare<Type>(lhs->types, rhs->types, [this](const Type* l, const
> Type* r) {

template <class T>
bool compare(const std::vector<const T*> lhs, const std::vector<const T*> rhs, std::function<bool(const T*, const T*)> comp) {
assert(lhs.size() == rhs.size());
for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
if (comp(*l, *r))
return false;
return true;
}

bool TypeChecker::isSubtype(const Types* lhs, const Types* rhs) {
return compare<Type>(lhs->types, rhs->types, [this](const Type* l, const Type* r) {
return this->isSubtype(l, r);
});
}

> Solution 2: avoid the lambda conversion by passing it as a template
> parameter:
>
> template <class T, class F>
> bool compare(const std::vector<const T*> lhs, const std::vector<const
> T*> rhs, F comp) {

From your description all I could think about was doing this as it needs this passing unless I use std::bind ? :-

template <class T, class K, typename C>
bool compare(const std::vector<const T*> lhs, const std::vector<const T*> rhs, K* k, C comp) {
assert(lhs.size() == rhs.size());
for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
if (k->comp(*l, *r))
return false;
return true;
}

bool TypeChecker::isSubtype(const Types* lhs, const Types* rhs) {
return compare<Type, TypeChecker, bool (TypeChecker::*)(const Type*, const Type*)>(lhs->types, rhs->types, this, &TypeChecker::isSubtype);
}

Could you please elaborate on solution 2 ?

Many thanks,

Aaron

Aaron Gray

unread,
Oct 4, 2023, 11:48:18 PM10/4/23
to
A slightly better attempt that gets the class but needs the following too :-

template<typename T> struct ClassOf {};
template<typename Return, typename Class>
struct ClassOf<Return(Class::*)> { using type = Class; };
template< typename T> using ClassOf_t = typename ClassOf<T>::type;


template <class T, typename C, class K = ClassOf<C>::type>
bool compare(const std::vector<const T*> lhs, const std::vector<const T*> rhs, K* k, C comp) {
assert(lhs.size() == rhs.size());
for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
if (k->comp(*l, *r))
return false;
return true;
}

bool TypeChecker::isSubtype2(const Types* lhs, const Types* rhs) {
return compare<Type, bool (TypeChecker::*)(const Type*, const Type*)>(lhs->types, rhs->types, this, &TypeChecker::isSubtype);
}

Many thanks,

Aaron

Bonita Montero

unread,
Oct 5, 2023, 2:48:19 AM10/5/23
to
If you pass your vectors by value and declare them const,
i.e. you usually won't modify it, you might rather pass
the vector a a const reference.

Aaron Gray

unread,
Oct 5, 2023, 7:26:54 AM10/5/23
to
Yes they should be passed by reference, I sorry missed doing that.

Aaron

Paavo Helde

unread,
Oct 5, 2023, 12:13:55 PM10/5/23
to
05.10.2023 06:37 Aaron Gray kirjutas:
> Hi Paavo,
>
> Okay got solution1 working, many thanks for that, I thought it would be something simple, now I am kicking myself :)
>
> But could not see how to invoke solution 2
[...]
>
> Could you please elaborate on solution 2 ?

Not sure what's there to elaborate, solution 2 was meant as an example
where the original call site code would work without any change.

I.e. the following compiles fine for me with VS2022:

#include <string>
#include <vector>
#include <functional>
#include <cassert>
template <class T, class F>
bool compare(const std::vector<const T*> lhs, const std::vector<const
T*> rhs, F comp) {
assert(lhs.size() == rhs.size());
for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
if (comp(*l, *r))
return false;
return true;
}

template <class T>
bool compare(const std::vector<const T*> lhs, const std::vector<const
T*> rhs, std::function<bool(TypeChecker*, const T*, const T*)> comp) {
assert(lhs.size() == rhs.size());
for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
if (comp(*l, *r))
return false;
return true;
}

bool TypeChecker::isSubtype(const Types* lhs, const Types* rhs) {
return compare(lhs->types, rhs->types, [this](const Type* l, const
Type* r) {
return this->isSubtype(l, r);
});
}

bool TypeChecker::isSubtype2(const Types* lhs, const Types* rhs) {
for (auto l = lhs->types.begin(), r = rhs->types.begin(); l !=
lhs->types.end(); ++l, ++r)
if (this->isSubtype(*l, *r))
return false;
return true;
}









Aaron Gray

unread,
Oct 5, 2023, 3:21:06 PM10/5/23
to
On Thursday, 5 October 2023 at 17:13:55 UTC+1, Paavo Helde wrote:
> 05.10.2023 06:37 Aaron Gray kirjutas:
> > Hi Paavo,
> >
> > Okay got solution1 working, many thanks for that, I thought it would be something simple, now I am kicking myself :)
> >
> > But could not see how to invoke solution 2
> [...]
> >
> > Could you please elaborate on solution 2 ?
> Not sure what's there to elaborate, solution 2 was meant as an example
> where the original call site code would work without any change.
>
> I.e. the following compiles fine for me with VS2022:

> template <class T>
> bool compare(const std::vector<const T*> lhs, const std::vector<const
> T*> rhs, std::function<bool(TypeChecker*, const T*, const T*)> comp) {
> assert(lhs.size() == rhs.size());
> for (auto l = lhs.begin(), r = rhs.begin(); l != lhs.end(); ++l, ++r)
> if (comp(*l, *r))
> return false;
> return true;
> }
>
> bool TypeChecker::isSubtype(const Types* lhs, const Types* rhs) {
> return compare(lhs->types, rhs->types, [this](const Type* l, const
> Type* r) {
> return this->isSubtype(l, r);
> });
> }

Oh great simple and obvious again :) My brains is obviously not thinking in C++ land again yet.

Just the TypeChecker* as the first parameter !

Many thanks,

Aaron

Aaron Gray

unread,
Oct 5, 2023, 5:42:42 PM10/5/23
to
Sorry ignore this my mistake !

Aaron
0 new messages