0

As the title says, how can I have the following intent expressed in code ? The requirements being the function pointer takes any type of arguments. Variadic doesn't work because of std::string.

https://godbolt.org/z/1E1szT

Note that I cannot directly use auto fp = <the lambda> because fp is a class member variable.

#include <iostream>

template<typename T1, typename T2>
void(*fp)(T1 t1, T2 t2) = [](auto a, auto b){
                              std::cout << a << "--"
                              << b << std::endl;
                          };

int main(){
    fp(1, 2);
    fp('a', 'b');
}
13
  • 1
    Template of function pointer is illegal in C++. Commented Jan 7, 2021 at 18:53
  • 1
    Can't you just use auto fp = [](auto a, auto b){ ... };? Why do you need a function pointer specifically? Commented Jan 7, 2021 at 18:56
  • @RemyLebeau it cannot be stored as a class member variable. Commented Jan 7, 2021 at 18:57
  • @puio: Yes it can, it's just slightly difficult. You have to take the lambda type as a class parameter. Also, remember that a lambda is merely syntactical sugar, you can just write the class yourself Commented Jan 7, 2021 at 18:59
  • @MooingDuck and then make the whole class a template ? i don't understand. If that's the case no .. that's no good. Commented Jan 7, 2021 at 19:01

2 Answers 2

1

A variable template is perfectly fine.
Even the way you initialize them is fine.

Just be aware it is a template for variables, not a variable storing a template (which cannot exist).

Thus when finally using it, things fall apart:

How should the compiler know which of the myriad potential instances you mean?
Simple, you have to actually tell it:

int main(){
    fp<int, int>(1, 2);
    fp<char, char>('a', 'b');
}

Of course, manually desugaring the lambda and getting an instance of it would be more practical:

struct {
    template <class T1, class T2>
    void operator()(T1 a, T2 b) const {
        std::cout << a << "--" << b << std::endl;
    };
} fp;

A shame we cannot simply give a lambda a name and let the compiler figure it out.
Brainstorming candidates for appropriate syntax:

struct A = []...;
struct B : []... {
};
using C = []...;
Sign up to request clarification or add additional context in comments.

4 Comments

Guess i should stop fighting the language now and improve the design itself.
for naming a lambda, use the same thing the langauge does for nullptr_t: auto lambda = []{}; using lambdaT = typeof(lambda);
@MooingDuck Yes, but that doesn't work too well outside functions...
@MooingDuck i posted a more complete question at stackoverflow.com/questions/65628159/…
0

You cannot have such a template function pointer, but remember that a lambda is merely syntactical sugar, you can just write the class yourself.

class fp {
    template<class AT, class BT>
    void operator()(AT&& a, BT&& b) {
        std::cout << a << "--" << b << std::endl; 
    };
};
class class_with_functionoid_member {
    fp fn_;
};

And the more generic version would be that you can have the lambda as a member. You have to take the lambda type as a class parameter.

template<class fnT> 
class class_with_lambda_member {
    fnT fn_;
public:
    class_with_lambda_member(fnT fn) : fn_(std::move(fn)) {}
};

The gist is that a function pointer is a type of runtime type erasure, and template member functions require compile time lazy instantiation, and C++ has no way to mix these two concepts. All the workarounds that come to mind revolve around explicitly listing out every possible combination of T1 and T2` in the "interface", which doesn't mix with a function pointer, but can work with a function pointer looking object.

struct erasable_functions {
    virtual ~erasable_functions(){}
    virtual void operator()(int, int)=0;
    virtual void operator()(int, char)=0;
    virtual void operator()(char, int)=0;
    virtual void operator()(char, char)=0;
};

template<class lambdaT>
struct erased_functions : erasable_functions {
    lambdaT lambda_;
    erased_functions(lambdaT lambda) : lambda_(std::move(lambda)){}
    virtual void operator()(int a, int b) {lambda_(a, b);
    virtual void operator()(int a, char b) {lambda_(a, b);
    virtual void operator()(char a, int b) {lambda_(a, b);
    virtual void operator()(char a, char b) {lambda_(a, b);
};
template<class lambdaT>
erased_functions<lambdaT> erase_functions(lambdaT lambda)
{return {std::move(lambda)};}

struct class_with_functionoid_member {
    erasable_functions* functions_;
    class_with_functionoid_member(erasable_functions* functions) : functions_(functions){}
    void operator()(int a, int b) {(*functions_)(a, b);
    void operator()(int a, char b) {(*functions_)(a, b);
    void operator()(char a, int b) {(*functions_)(a, b);
    void operator()(char a, char b) {(*functions_)(a, b);
};

int main() {
    auto lambda = erase_functions([](auto a, auto b) {
            std::cout << a << "--" << b << std::endl;
        };
    class_with_functionoid_member c(&lambda);
}

3 Comments

He has such a variable template. Of course, using it is plenty cumbersome, and it either wasn't really what he wanted, he didn't know what he wanted, or he just made a poor choice. At least most probably.
Technically he has a template for pointers, which doesn't solve his problem
Hey, i posted a new question with more details: stackoverflow.com/questions/65628159/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.