1

I have a function which looks like foo in the following example:

template <typename... Parameters>
void foo(std::function<void (Parameters &)>... functions) {
    // does interesting things with these functions
}

Now I want to call this function with some lambdas, e.g. like this:

foo([](const std::string & string) {});

Unfortunately that doesn't work, because I get the following error:

error: no matching function for call to 'foo'
note: candidate template ignored: could not match 'function<void (type-parameter-0-0 &)>' against '(lambda at file.cpp:50:23)'

AFAIK, that is, because lambdas cannot be implicitly converted to std::functions like that.

One way to solve this problem is to manually wrap the lambdas in std::function like so:

foo(std::function<void (const std::string &)>([](const auto & string) {}));

But for multiple lambdas this would get very tedious.

To get around this problem, I tried to create a wrapper function which detects the parameter type of the lambdas it gets passed using a helper type, and then wraps the lambda in the correct std::function type. Here is this wrapper function for only a single parameter (i.e. not variadic):

template <typename Function>
void fooWrapped(Function && function) {
    foo(std::function<void (typename FunctionTypeTraits<Function>::ParameterType &)>(function));
}

The helper type FunctionTypeTraits is implemented like this:

template <typename Function>
class FunctionTypeTraits:
    public FunctionTypeTraits<decltype(&std::remove_reference<Function>::type::operator())> {};

template <typename Param>
class FunctionTypeTraits<void (&)(Param &)> {
    typedef Param ParameterType;
};

Now I can call the wrapper function with my lambda and the compiler is perfectly happy:

fooWrapped([](const std::string & string) {});

In principle, I should now be able to make fooWrapper variadic like so:

template <typename... Functions>
void fooWrapped(Functions &&... functions) {
    foo((std::function<void (typename FunctionTypeTraits<Functions>::ParameterType &)>(functions))...);
}

That doesn't work however. If I call this new function with the exact same code, I get the following error:

error: 'std::remove_reference<void ((lambda at file.cpp:50:23)::*)(const std::string &) const>::type' (aka 'void ((lambda at file.cpp:50:23)::*)(const std::string &) const') is not a class, namespace, or enumeration

I don't quite understand this error. Why does the same approach work for a single template type, but not for an expanded parameter pack? Is this maybe just a compiler bug?
Is there another way, I could achieve my goal of calling foo using lambdas, without manually wrapping each of them in a std::function?

Josef Zoller
  • 921
  • 2
  • 8
  • 24

1 Answers1

1

The type of address of lambda's operator() is void (Lambda::*)(Param&) const not void (&)(Param &), you need to define the base case of your FunctionTypeTraits as:

template <typename Function>
struct FunctionTypeTraits:
  public FunctionTypeTraits<decltype(&std::remove_reference<Function>::type::operator())> {};

template <typename Lambda, typename Param>
struct FunctionTypeTraits<void (Lambda::*)(Param) const> {
  typedef Param ParameterType;
};

Another point is that in your fooWrapped, the type of specify for std::function should be void (typename FunctionTypeTraits<Function>::ParameterType) instead of just ParameterType since the latter is not a function type:

template <typename... Function>
void fooWrapped(Function&&... function) {
  foo(std::function<void (typename FunctionTypeTraits<Function>::ParameterType)>(function)...);
}

Demo.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • Oh ok. Your second point was actually just a mistake on Stackoverflow and not actually how I tried it. I will edit my question to correct this. But with the first it seems to work now. I'm curious as to why it worked with the non-variadic function but not with parameter packs... – Josef Zoller Nov 14 '21 at 03:09
  • I haven't seen it [work](https://godbolt.org/z/jrG3szxnd), can you provide a reproduced example? – 康桓瑋 Nov 14 '21 at 03:18
  • Apparently my IDE shows the errors with the variadic example but not with the non-variadic one...I could only see them once I built the code. I should really test things before I claim them to be true...sorry about that. And thanks for your help! – Josef Zoller Nov 14 '21 at 03:41