Как создать вектор, заполненный указателями на функции в стиле C и лямбдами (с захватами и без них) - PullRequest
0 голосов
/ 16 октября 2019

В последнее время я узнал о лямбдах и указателях функций и хотел использовать их в простой системе обратного вызова. Карта, которая хранит событие и все обратные вызовы, которые должны быть вызваны при его срабатывании, выглядит следующим образом:
std::unordered_map< sf::Event::EventType , std::vector< Callback > > eventMap;.

Я определил Callback таким образом typedef void(*Callback)(sf::Event const&);. Моя registerEvent() функция принимает событие и обратный вызов в качестве аргументов.
registerEvent() может быть вызван следующим образом:

inputHandler.registerEvent(/*Any event type*/, [](sf::Event const&)
{
    //do something
});

Это прекрасно работает, но когда я хочу захватить объект, я получаюошибка компиляции: error: no matching function for call to ‘InputHandler::registerEvent(sf::Event::EventType, Button::Button(ID, Callback, InputHandler&)::<lambda(const sf::Event&)>)’ Код, который выдает эту ошибку, выглядит следующим образом:

class Button
{
//
public:
    Button(ID id, Callback callback, InputHandler& inputhandler)
    {
        inputhandler.registerEvent(sf::Event::MouseButtonPressed, [this](sf::Event const&)
        {
            {
              getTextureRect();  
            }
        });
    }
};

Кажется, что захват this изменяет тип лямбда-выражения, что делает его несовместимым с registerEvent() функция. Но так как множество разных классов должны иметь возможность создавать такие лямбды, а также лямбды без захвата должны содержаться в векторе, я не знаю, как определить, какого типа должен быть вектор. Я слышал о std::function, но (это может звучать глупо), я хотел бы использовать способ в стиле C, чтобы лучше понять. Я думаю, что этот пост говорит кое-что об этом, но я не совсем понимаю это хорошо. Я думал о создании двух карт, одна для всех лямбд без захвата и одна для других. Тогда я мог бы позволить каждому классу наследовать от базового класса и сделать тип захвата в векторе указателем на базовый класс, хотя это довольно сложно и добавляет ненужные накладные расходы, и я даже не знаю, сработает ли это. Каково общее решение для подобной ситуации?

Спасибо за все ответы и с наилучшими пожеланиями,

Даниэль Шрайбер Мендес:)

Редактировать:
Обратный вызов теперь выглядит следующим образом: typedef std::function< void(sf::Event const&) > Callback; И когда я хочу его использовать:

//inside the Button Constructor
inputhandler.registerEvent(sf::Event::MouseButtonPressed, Callback([this](sf::Event const& event)
        {
            //memberFunc();
        }));

Это не дало мне ошибку, но вел себя неправильно. Он действительно выполнял лямбду, но не изменял класс, в котором он был определен. Поэтому я добавил строку в конструктор, которая просто печатает адрес только что созданного объекта. Я создаю только одну кнопку, поэтому должен быть напечатан только один адрес. Но было два разных. Это означает, что при вставке лямбда-выражения, обернутого в std :: function, в вектор создается новый объект Button. Как это может быть? Неявное создание объекта?

Спасибо за ваши ответы:)

1 Ответ

2 голосов
/ 16 октября 2019

Лямбды с захватами не конвертируются в указатели функций. У простого указателя функции нет места для хранения состояния для лямбда-захватов.

std::function - это объект, поэтому он может хранить лямбда с захватами или без них.

Если необходимо использоватьнеобработанные указатели на функции, затем вам нужно сохранить указатель на функцию и состояние, а затем передать состояние функции при ее вызове. например,

typedef void(*Callback)(sf::Event const&, void* data);
std::unordered_map< sf::Event::EventType , std::vector< std::pair< Callback, void* > > > eventMap;

Затем вам нужно сохранить свое состояние в объекте, сохранить объект где-нибудь, чтобы он оставался живым. Внутри вашего объекта вы должны привести от void* к указателю вашего состояния. Если вы не используете библиотеку AC, которая требует простых указателей на функции, тогда на самом деле гораздо проще просто использовать std::function, который сделает все это за вас.

...