C ++ Хранение функций обратного вызова внутри Struct Vector - производительность, параметры вызова, проблемы выделения ресурсов - PullRequest
0 голосов
/ 04 октября 2018

В настоящее время я экспериментирую с разработкой класса InputHandler, который будет отвечать за хранение функций обратного вызова, зарегистрированных в его экземпляре из различных объектов, и вызов этих функций при возникновении необходимых действий и событий.Для этого у меня есть такая структура: (отредактировал и написал всю структуру)

struct EventContainer {

public:
    EventContainer();
    EventContainer(EventType t, SDL_Scancode k, std::function<void()>&& cb, int pid) : Type(t), m_Key(k), Callback(cb), m_ID(pid) {}
    EventContainer(const EventHolder& c) : Type(c.Type), m_Key(c.m_Key), Callback(c.Callback), m_ID(c.m_ID) {}

    int m_ID;
    std::function<void()> Callback;
    EventType Type;
    SDL_Scancode m_Key;

private:

    friend bool operator==(const EventContainer& lhs, const EventContainer& rhs)
    {
        return lhs.m_ID == rhs.m_ID;
    }

};

std::vector<EventContainer> eventCallbacks; // Vector to store all event callbacks.

Пока все хорошо, тогда я создал метод для привязки определенного события.

void BindMethod(std::function<void()>&& callback, // other params for struct)
{ 
     EventContainer cb;
     cb.Callback = callback;
     // other params

     eventCallbacks.push_back(std::move(cb));
}

В основном цикле я проверяю вектор eventCallbacks в соответствии с другими его параметрами, и если мне нужны истинные условия, я запускаю соответствующий Callback.Пока у меня нет проблем с этим.Теперь прибывает моя пара проблем и вопросов.

Существует 3 способа, насколько я могу проверить, что я могу вызвать этот BindMethod, чтобы зарегистрировать обратный вызов.Мне интересно, чем они отличаются?Особенно в отношении памяти, и я также, конечно, хотел бы узнать, почему.

Первый метод:

  input->BindMethod(&TestMethod1); // I have a simple method, returning void, with no params called TestMethod1

Работает замечательно, однако я не могу передать никаких параметров.Конечно, всегда есть возможность создания шаблонов и поддержки нескольких аргументов, но я не хочу чрезмерно усложнять вещи с помощью модульности или «универсальности».Мне просто нужно получить то, что мне нужно.

Итак, есть второй вариант ...

auto f = std::bind(TestMethod2, 5, 2); // 5,2 are my params for TestMethod2.
input->BindMethod(f);

Этот тоже работает замечательно, однако я не создал много тестовых случаев для этого.только базовые, так что не могу точно сказать.Плюс из того, что я исследовал, сообщество не любит std :: bind так сильно.

И там идет волшебная лямбда, третий путь

auto f2 = [testInteger] { TestMethod3(testInteger); };
input->BindMethod(f2);

Лямбда-способ работы вещей работаетдовольно круто, однако, я подозреваю, что в этом случае у меня возникает вопрос: что если экземпляр объекта, который содержит функцию-член внутри лямбда-тела, умирает до того, как событие когда-либо сработает, и будет вызван обратный вызов, и хуже, что еслиобратный вызов будет вызван после того, как объект умер?Таким образом, мы должны реализовать какой-то механизм, который выведет соответствующую структуру обратного вызова из векторного массива, верно?Однако, насколько я видел, удалить структуру из векторного массива довольно проблематично.Сначала я дал несколько идентификаторов для объектов struct, затем я попытался сделать это внутри определенного метода привязки:

void Unbind(int pid)
{
EventCallbacks.erase(std::remove_if(EventCallbacks.begin(), EventCallbacks.end(), [&](EventContainer const & holder) {return holder.id == h.GetID(); }), EventCallbacks.end());
}

Однако моя программа потерпела крах.Я проверил pid, который передается правильно, и я проверил, что holder.id является правильным и соответствует pid, но каким-то образом программа вылетает, вероятно, что-то связанное с итератором, и не может найти причину.

Итак, подведем итоги:

  1. Принципиальные различия между этими тремя методами для вызова метода BindMethod.И если есть еще варианты, которые можно использовать, или какие-либо идеи, мысли?
  2. Является ли это хорошим способом организации обработки обратного вызова события?У меня есть структура с методом обратного вызова и всеми остальными данными, которые мне нужно проанализировать, и вектор этих структур для вызова методов при необходимости.
  3. Мне определенно нужен надежный и конкретный способ поиска иУдаление элемента вектора без сбоя, также необходимо понять, почему происходит сбой.Изменить: немного больше исследований, вероятно, из-за смещения вектора вызывает после удаления.Однако, поскольку я обнаружил, что списки не вызывают смещения, существует более линейный подход к вставке и удалению.Итак, было бы лучше переключиться на списки вместо векторов?Поскольку мне не нужно иметь произвольный доступ к какому-либо элементу внутри моей коллекции, все, что я буду делать, это итерировать его с постоянной частотой кадров.
  4. Как насчет опасностей в средствах выделения памяти при использованииstd :: function?Безопасно ли основывать мою архитектуру или я должен пойти по более лучшему пути?
  5. Есть ли какие-либо шаги, которые я должен предпринять при удалении объектов структуры из вектора с помощью средствосвободить память?Есть ли что-то, что я должен быть осторожным?
  6. Я действительно чувствую себя не очень комфортно, когда использую std :: vectors или вообще что-то в std.Существуют ли другие дешевые, функциональные подходы?

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

...