5

I wrote some code that retrieves the types of the non-auto parameters when given a generic lambda function. As you can see in the code below, the idea is to call the connect function with a generic lambda and provide arguments for the auto parameters (which will always be at the front in my use case). So in the code below my goal was to detect that the second parameter is of type float.

The code works fine with clang 3.8 but it doesn't compile with gcc 6.1.1, so I was wondering whether this was a bug in gcc or if this is just not valid c++ code? Can I assume that a generic lambda is implemented with a templated operator() function or is this compiler-specific?

template <typename Functor, typename... AllArgs, typename... ProvidedArgs>
void findArgTypes(void(Functor::*)(AllArgs...) const, Functor, ProvidedArgs...)
{
    // AllArgs == int, float
    // ProvidedArgs == int
}

template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
    findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
}

int main()
{
    int tmp = 0;
    connect([&](auto, float){ ++tmp; }, 0);
}

The error that gcc gives is this:

main.cpp: In instantiation of ‘void connect(Func, ProvidedArgs ...) [with Func = main()::<lambda(auto:1, float)>; ProvidedArgs = {int}]’:
main.cpp:16:33:   required from here
main.cpp:11:17: error: no matches converting function ‘operator()’ to type ‘void (struct main()::<lambda(auto:1, float)>::*)() const’
     findArgTypes(&Func::template operator()<ProvidedArgs...>, func, providedArgs...);
     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:27: note: candidate is: template<class auto:1> main()::<lambda(auto:1, float)>
     connect([](auto, float){}, 0);
                           ^

Removing the const in findArgTypes gives the same result.

Using the following code works with both compilers:

struct Foo
{
    template <typename T>
    void operator()(T, float) const {}
};

int main()
{
    Foo f;
    connect(f, 0);
}
texus
  • 291
  • 1
  • 3
  • 12

1 Answers1

1

You have error because you are expecting functor (object) but lambda with empty capture is convertible to free function:

int main() {
    using function = void (*)(int, float);
    function a = [](auto, float){};
}

See lambda from cppreference:


For the newest version of your question that implementation satisfies both compilers:

template <typename Func, typename... ProvidedArgs>
void connect(Func func, ProvidedArgs... providedArgs)
{
    auto mf = &Func::template operator()<ProvidedArgs...>;
    findArgTypes(mf, func, providedArgs...);
}

I think this is gcc compiler bug that gcc needs this auto local variable to work correctly...

BTW, one question - one bug in clang, one in gcc - I really advice you to find simpler way to achieve your goals - maybe consider to just use std::function instead of quite fresh generic-lambda?

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • That code seems to compiles with gcc but not with clang. But changing my code to take `void(*)(AllArgs...)` as first parameter of findArgTypes still gives the same error with gcc (and no longer works with clang). Capturing a local variable also doesn't seems to make a difference. – texus Jun 22 '16 at 15:53
  • You need two overloaded functions - one for free functions, one for object functions - or just change a little your design... – PiotrNycz Jun 22 '16 at 16:03
  • clang could be wrong - see example from linked page: `int& (*fpi)(int*) = [](auto* a)->auto& { return *a; }; // ok` – PiotrNycz Jun 22 '16 at 16:10
  • The real code is a lot more complex and actually does handle free functions already. I'll update the question to capture a variable. The example from the linked page does work, but not the code you posted for some reason (it says it can't convert from the lambda to the function type). But that's an unrelated problem. – texus Jun 22 '16 at 16:14
  • Looks like you are trying to reimplement http://en.cppreference.com/w/cpp/utility/functional/invoke from C++17? Am I right? – PiotrNycz Jun 22 '16 at 16:40
  • That sounds like it might be a clang bug. If I use an approximately equivalent handwritten functor, the conversion works: http://coliru.stacked-crooked.com/a/6e2ba84e61622b91 – melak47 Jun 22 '16 at 16:43
  • @PiotrNycz I'm rewriting a callback system where you can leave parameters unbound (not provided when calling connect) and where the code detects what parameters are to be filled in with information about the callback. For instance when a button is clicked, the connected function would be called with the text of the button as argument if the function had an unbound std::string parameter. If all arguments were provided to connect then the function is called without this string. So the code is all about detecting the types of the parameters that the user doesn't provide when calling connect. – texus Jun 22 '16 at 16:57
  • Splitting it in 2 lines solved the issue, thanks. I'm mostly just playing around with this code though, it isn't something that I really need. And since I also have to keep VC++ supported I doubt the code that I am writing now is going to be used anywhere soon. – texus Jun 22 '16 at 17:39
  • Ok. Ibelieve compiler vendors need so strong testers of their products. I also like to explore new and dark areas of c++... ;) – PiotrNycz Jun 22 '16 at 18:37