Home > front end >  Casting parameter-less generic lambda to a function pointer
Casting parameter-less generic lambda to a function pointer

Time:07-30

I'm trying to cast a parameter-less generic lambda to a function pointer. This problem generally applies to generic lambdas, which parameters don't depend on the template argument.

Example tries to cast the lambda with one parameter (int) which is typewise independent of the template parameter T.

#include <iostream>

int main()
{
    auto lambda = []<class T>(int v) -> void
    {
        std::cout << typeid(T).name() << ' ' << static_cast<T>(v) << '\n';
    };

    // How do I cast lambda to a function pointer that uses operator(int)<char>
    // This doesn't compile, I've reached this conclusion after examining this page https://en.cppreference.com/w/cpp/language/lambda
    auto seeked_ptr_char = lambda.operator fptr_t<char>();

    // Hacky solution :(
    auto mptr_char = &decltype(lambda)::operator()<char>;
    decltype(mptr_char) mptr_float = &decltype(lambda)::operator()<float>;

    (lambda.*mptr_char)(48);
    (lambda.*mptr_float)(52);

    // This is okay (tho parameter is dependent on a template argument, which is not what we are looking for)
    auto another_lambda = []<class T>(T v) -> void
    {
        std::cout << v << '\n';
    };

    void(*ptr_char)(char) = another_lambda;
    ptr_char(50);
 
    return 0;
}

Doesn't compile with x86-64 gcc 12.1 with -std=c 20 -O3

x86-64 clang 14.0.0 -std=c 20 -O3

https://godbolt.org/z/cavbT5jM3

CodePudding user response:

How do I cast lambda to a function pointer that uses operator(int)<char>

TL;DR: you don't.

First, function pointers point to functions. Templates are not functions yet; a function template only becomes a function when you supply it with template parameters. A function pointer can point to a specific instantiation of a function template. But it cannot point to a function template.

The conversion from a generic lambda to a function pointer relies on invoking a template conversion functions. As such, the lambda effectively has a conversion function like this:

using func = void(int);

template<typename T>
operator func*();

However, that's a template conversion function. And since the type being converted to does not supply the template parameter, in order to call that function, you must explicitly provide that type. Which means you have to explicitly call the conversion function.

But given the code you wrote, you worked all of that out. Which means your real problem is:

How do I explicitly invoke a template conversion function if the result of the conversion is in no way related to the template parameter(s)?

Lambdas don't matter here. This is all about calling a special kind of template conversion function.

And the answer is... apparently, you don't.

You can explicitly invoke a conversion function to the type type_name via object_name.operator type_name() syntax. The problem is this line in the standard:

The conversion-type-id in a conversion-function-id is the longest sequence of tokens that could possibly form a conversion-type-id.

The problem is how lambda.operator fptr_t<char>() gets parsed. See, fptr_t could be the name of a template class. And therefore, fptr_t<char> could be the name of a specialization of that class template. Therefore fptr_t<char> is "the longest sequence of tokens that could possibly form a conversion-type-id".

In short, the compiler thinks you're trying to invoke the conversion function to the type fptr_t<char>. It does not matter that fptr_t is not a template, and the compiler can look around and figure that out. Parsing happens before any of that kind of thinking. So the parsing rules take priority. The entire fptr_t<char> text is taken as the typename for the conversion function.

This all means that, unless the template parameter(s) for a conversion function can be deduced from the type being converted to alone, it appears that it is impossible to call such a conversion function. Such a conversion function can exist; you can declare such functions just fine. But C lacks any syntax for actually interacting with it.

Yet another reason why lambdas with template parameters that cannot be deduced from the function arguments are not especially useful.

CodePudding user response:

Thanks for @Nicol pointing it out, my solution is not a function pointer. It needs more edition..


You can convert it to a std::function through another lambda function as a wrapper, I have no idea how it can be further converted to a function pointer though,

auto wrapper = []<typename T, typename F>(F&& f) {
    return [f = std::forward<F>(f)](auto&&... args) {
        f.template operator()<T>(std::forward<decltype(args)>(args)...);
    };
};

std::function<void(int)> ptr_char = wrapper.operator()<char>(lambda);

ptr_char(48);

Demo

  • Related