0

I'm trying to create the following class that I can use to test objects to see if they pass some filter.

template <typename T>
class Filter
{
public:
  typedef bool (*Functor)(const T&);

  bool test(const T& t) {
    return func(t);
  }

  void setTester(Functor f) {
    func = f;
  }

private:
  Functor func;
};

If I just create a method in the global namespace this works fine.

bool testFunc(const Object& obj)
{
  return !obj.name().isEmpty();
}

void foo(const Object& obj)
{
  Filter<Object> filter;
  filter.setTester(testFunc);
  filter.test(obj);
}

But what I'd really like is to use lambda functions so that I don't have to create all of my filter methods like this. I want to be able to create them on the fly.

void foo(const Object& object)
{
  Filter<Object> filter;

  filter.setTester([](const Object& object) {
    return !object.name().isEmpty();
  });

  filter.test(object);
}

But when I do this I get the following compile error.

C2664: 'ov::Filter<T>::setTester' : cannot convert parameter 1 from 'ov::`anonymous-namespace'::<lambda0>' to 'bool (__cdecl *)(const T &)'
with
[
    T=ov::Object
]
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

I've read other posts about passing lambda functions as function pointers like this one and I understand that they cannot capture in order for this to work. That's fine, and you can see I'm not trying to capture anything.

I am compiling my code using VS2010.

So what am I doing wrong?

Community
  • 1
  • 1
Moohasha
  • 245
  • 1
  • 13
  • I've gotten around this in the past by making the method a template so that it derives the type for me, but I can't do that in this case because I'm copying the function pointer to a member variable to use later. – Moohasha Nov 05 '15 at 15:23
  • 1
    Have a look at [std::function](http://www.cplusplus.com/reference/functional/function/) – Mohamad Elghawi Nov 05 '15 at 15:24
  • 2
    Can you provide a [minimal, complete, and verifiable example](http://www.stackoverflow.com/help/mcve)? I cannot reproduce. Are you sure your lambda isn't capturing something? – Barry Nov 05 '15 at 15:26
  • 1
    Works fine for me http://coliru.stacked-crooked.com/a/f488aa72edf6f941 – 101010 Nov 05 '15 at 15:39
  • Have you tried to use + before your lambda ? like : `+[](){}` to force the degradation to a function pointer : example http://coliru.stacked-crooked.com/a/9a9559bf5b56637d – Pumkko Nov 05 '15 at 15:40
  • What compiler are you using? What are the flags being passed to it? What is the type of `object.name().isEmpty()`? – Yakk - Adam Nevraumont Nov 05 '15 at 16:51
  • I'm using VS2010 and that appears to be the problem. =\ – Moohasha Nov 05 '15 at 17:01
  • @Moohasha I am surprised VS2010 supports lambda. – Yakk - Adam Nevraumont Nov 05 '15 at 17:03

3 Answers3

1

This worked fine for me, but provided that the class Object is actually defined somewhere, with a member name() returning a string, in which case I use empty() instead of isEmpty().

I successfully tested the following code:

// class Filter: no modification
class Object { public:string name() const { return "name"; } };

bool foo(const Object& obj)
{ Filter<Object> filter;
  filter.setTester([](const Object& obj) { return !obj.name().empty();});
  return filter.test(obj);
}
int main()
{ Object c; cout << foo(c); 
  system("pause");
}

Ouput: 1

p.s: i am using VS2015.

A.S.H
  • 29,101
  • 5
  • 23
  • 50
  • It appears to be a compiler issue. When I switched to VS2015 it compiled just fine, but I'm currently using VS2010 so I guess it's not supported. – Moohasha Nov 05 '15 at 17:00
  • @Moohasha That is highly likely. AFAIK VS2010 is not fully compatible with C++11 and there are a few patches to istall, so this shouldn't be surprising. – A.S.H Nov 05 '15 at 17:02
1

Your compiler likely doesn't support this. You're using MS, so a lot of these things will be missing. They got most the important stuff in the more recent ones, but go back even a little and lots is missing. I don't think they support this even now.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • Converting a lambda to a function pointer is something that works in at least some versions of MSVC compilers... I think there might have been a bug in a pre-release 2015 compiler, but even that should work with an implicit conversion. – Yakk - Adam Nevraumont Nov 05 '15 at 16:55
  • You're right. I'm using VS 2010. If I try to build it in VS 2015 then it compiles just fine. – Moohasha Nov 05 '15 at 16:59
1

What happens if you explicitly set the output type?

filter.setTester([](const Object& object) -> bool {
  return !object.name().isEmpty();
});
NicolaSysnet
  • 486
  • 2
  • 10
  • Heh, I think this might be the problem; except, shouldn't `!` return `bool`? – Yakk - Adam Nevraumont Nov 05 '15 at 16:54
  • `!` returns `bool` only if the object it is applied to does not overload it. In this case the compiler would have to infer that for every `object` you pass to the function the method `isEmpty()` of the object returned by its method `name()` does not overload the operator. Hard to understand, no? – NicolaSysnet Nov 05 '15 at 17:00
  • No, the compiler just has to check the specific types in play. The return value of that lambda is `std::decay_t< decltype( !std::declval().name().isEmpty() ) >`. Once determined, that is fixed. Then it is passed to `setTester`, and the overload to convert-to-function should be found. This fails to work if *with the actual types*, `!blah` isn't exactly `bool`. But if `name()` is a `CString` (as it appears to be), `isEmpty()` returns `BOOL`, and `!` would convert to `bool`. – Yakk - Adam Nevraumont Nov 05 '15 at 17:03
  • We don't have the code for `Object`... so we cannot tell many thing about `name()`. Ask the OP to give us details about that class. – NicolaSysnet Nov 05 '15 at 17:16