Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting parameter types from generic lambda - compile error with GCC

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);
}
like image 384
texus Avatar asked Nov 09 '22 13:11

texus


1 Answers

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?

like image 133
PiotrNycz Avatar answered Nov 14 '22 23:11

PiotrNycz