问题
Ive been learning about lambdas and function pointers lately and wanted to use them in a simple callback system.
The map that stores an event and all callbacks that should be called when it gets triggered looks like this:std::unordered_map< sf::Event::EventType , std::vector< Callback > > eventMap;
.
I defined Callback this way
typedef void(*Callback)(sf::Event const&);
. My registerEvent()
function takes an event and a Callback as arguments.registerEvent()
can then be called this way:
inputHandler.registerEvent(/*Any event type*/, [](sf::Event const&)
{
//do something
});
That works fine but when I want to capture an object I get an compile error saying
error: no matching function for call to ‘InputHandler::registerEvent(sf::Event::EventType, Button::Button(ID, Callback, InputHandler&)::<lambda(const sf::Event&)>)’
The code that produces that error is the following:
class Button
{
//
public:
Button(ID id, Callback callback, InputHandler& inputhandler)
{
inputhandler.registerEvent(sf::Event::MouseButtonPressed, [this](sf::Event const&)
{
{
getTextureRect();
}
});
}
};
It seems as if the this
capture is changing the type of the lambda, which makes it incompatible with the registerEvent()
function. But since lots of different classes should be able to create such lambdas and also lambdas without any capture should be contained in the vector, I don't know how to tell what type the vector should be. I heard of std::function
but (this may sound silly) I would like to use the C-Style way in order to get a better understanding. I think this post says something about that but I don't quite understand it that well. I thought about creating two maps, one for all capture-less lambdas and one for the others. Then I could let every class inherit from a base class and make the capture type in the vector a pointer to the base class, though this is pretty complex and adds unnecessary overhead and I don't even know if this would work.
What is the general solution for a situation like this one?
Thanks for all replies and best regards,
Daniel Schreiber Mendes :)
Edit:
Callback now looks like this:
typedef std::function< void(sf::Event const&) > Callback;
And when I want to use it:
//inside the Button Constructor
inputhandler.registerEvent(sf::Event::MouseButtonPressed, Callback([this](sf::Event const& event)
{
//memberFunc();
}));
This did not give me an error but behaved in a wrong way. It did execute the lambda, but it didn't change the class in which it was defined. So I added a line in the constructor which just prints the address of the just created object. I am creating only one Button, so there should be only one adress printed. But there were two different. That means when inserting the lambda wrapped in a std::function into a vector, a new Button object gets created. How can that be? An implicit Object creation?
Thanks for your answers :)
回答1:
Lambdas with captures aren't convertible to function pointers. A plain function pointer hasn't got anywhere to store the state for the lambda's captures.
std::function
is an object so can store a lambda with or without captures.
If you must use raw function pointers then you need to store a function pointer and a state then pass the state to the function when you call it. e.g.
typedef void(*Callback)(sf::Event const&, void* data);
std::unordered_map< sf::Event::EventType , std::vector< std::pair< Callback, void* > > > eventMap;
You then need to store your state in an object, store the object somewhere so that it stays alive. Inside your object you need to cast from void*
back to your state object pointer. If you aren't using a c library that requires plain function pointers then it really is much simpler to just use std::function
which does all this for you.
来源:https://stackoverflow.com/questions/58420213/how-to-create-a-vector-filled-with-c-style-function-pointers-and-lambdaswith-an