0
votes

I would like to code a utility function pointer inside my object that calls a group of other functions in the object. This utility function pointer should be initialized with default functionality, but be capable of override as well.

This is what I have so far:

struct GameState {
    void(*setup)()          = [](){};
    void(*handleInput)()    = [](){};
    void(*update)()         = [](){};
    void(*draw)()           = [](){};
    void(*run)() = [this]() { handleInput(); update(); draw(); };
}

The compiler doesn't like the use ofthisin the capture of my lambda function and spits out the following errors all for that line:

E0413 - no suitable conversion function from "lambda []void ()->void" to "void (*)()" exists

C2440 'initializing': cannot convert from 'GameState::' to 'void (__cdecl *)(void)'

C2439 'GameState::run': member could not be initialized

1
It will be possible to use type-erasure with std::function, however this will create a host of other problems, making your class uncopyable and/or unassignable. This is an XY problem. What is your real problem? No, not the one you're asking about, how to make function pointers work this way. But the real problem to which you believe the solution is to make function pointers work this way. This is the wrong solution. If you explain what your real problem is, I'm sure a real solution exists. - Sam Varshavchik
Whiy not plain old virtual functions? - n. 1.8e9-where's-my-share m.
@n.m. I don't know what is the OP had in mind, but I have seen use cases for lambda function like these. In some cases lambdas can be used to eliminate if..else. For example in LazyInit classes, the get() method can actually be a lambda. It is more common in C#, and I'm not sure if it is a good design or bad - Michael Veksler
@MichaelVeksler's answer with the std::function<> suggestion achieves the desired outcome. I'm not sure how I would override virtual functions without making derived classes/structs. Creating a unique derived class for each code variation seems inefficient. - Enc0der
Yes you need to create a derived class. I don't think it's that inefficient, in terms of lines of code. About two lines per class doesn't seem excessive. In terms of run time performance it is potentially more efficient. It's also a bit more flexible. - n. 1.8e9-where's-my-share m.

1 Answers

3
votes

The problem is that the types mismatch.

void(*run)() = [this]() { handleInput(); update(); draw(); }; }

You are assigning a lambda to a function pointer. A lambda is not a function pointer, and is actually an instance of a class that overrides operator(). This object also captures this in your example.

The

void(*setup)() = [](){};

Compiles because there is no capture. When there is no capture, there is no state and the lambda can be treated as a pointer and not as a class instance.

If it was allowed by the language, you'd like something like

// error
auto run  = [this]() { handleInput(); update(); draw(); };ยท

But that syntax is not supported in this context. And since the type of the lambda object is unknown to the programmer, there is no way to write its type.

Instead, what will work is something like:

std::function<void()>run  = [this]() { handleInput(); update(); draw(); };

This is not as efficient as defining run with the type of the lambda, since std::function will most likely store the lambda on the heap.