Я недавно вернулся к C++ после многих лет работы на C# и java, и мне нравится, куда C++ ушел в мое отсутствие (начиная с C++11, теперь я впитываю C++20!). Сила шаблонов действительно захватывает меня. Однако я быстро столкнулся с чем-то, с чем, кажется, невозможно справиться.
Я написал систему событий на основе шаблонов, которой очень доволен.
struct imgui_event_listener
{
void onCreatedWindow(events::ImGui_CreatedWindow f)
{
}
}
dispatcher::connect(&listener, &imgui_event_listener::onCreatedWindow);
...
dispatcher::fire(events::ImGui_CreatedWindow{ window });
Мне это нравится, не требует параметров шаблона, может использовать произвольные структуры для событий. Однако есть одна большая загвоздка, и это почти бесконечные и совершенно непознаваемые потенциальные невидимые версии моей системы событий, использующие несколько unordered_maps, которые должны куда-то идти. В настоящее время они статичны внутри методов.
Такие вещи, как...
template <typename ListenerType, typename Type>
using FPtr = void(ListenerType::*)(Type);
template<typename ListenerType, typename Type>
using Tuple = eastl::tuple<ListenerType*, FPtr<ListenerType, Type>>;
template <typename ListenerType, typename Type>
using ListenerMap = eastl::unordered_map<uint32_t, Tuple<ListenerType, Type>>;
class EventSystem
{
template<typename ListenerType, typename Type>
ListenerMap<ListenerType, Type>& listenerMap()
{
static ListenerMap<ListenerType, Type> listenerMap;
return listenerMap;
}
}
Я относительный нуб в эти дни, так что простите, если что-то странное.
Это КАЖЕТСЯ слишком удобным и удовлетворительным, чтобы его можно было рекомендовать, я ожидал, что люди по какой-то причине будут перекрикиваться, но, похоже, именно так это по необходимости делается большую часть времени.
Но игровой движок, с которым я работаю, имеет диспетчер памяти и не использует и не рекомендует STL, имеет собственную замену, которая использует тот, который EA предлагает для игр с открытым исходным кодом, и имеет собственный распределитель, который следует использовать. для этого, чтобы он мог отслеживать память.
Это отслеживает все использование памяти и сообщает с исключением обо всех утечках, которые происходят во время удаления статических объектов.
К сожалению, несмотря на то, что контейнеры являются статическими, узлы этих unordered_maps выделяются из кучи, управляемой памятью. Ожидаемое использование будет состоять в том, чтобы очистить их при очистке, что происходит везде.
Теперь, я подозреваю, что на самом деле эти статические экземпляры карт будут автоматически обработаны после того, как диспетчер памяти уже пожаловался, однако на самом деле нет реального способа избежать его жалобы, даже если только в отладке, исключение при выходе и просмотр адресов памяти, о которых сообщается поскольку утечки раздражают, и мне становится стыдно, когда это происходит! Это также может скрыть реальные проблемы. Я уверен, что смогу перебрать нормальные карты STL с аллокациями, но я действительно хочу следовать по пути, проложенному разработчиками движка, и избежать головной боли в будущем. Так что это больше об удобстве и тщеславии, чем о чем-либо еще, но мне нужен способ обойти это, не жертвуя элегантностью использования моей системы событий.
Это привело меня в мир метапрограммирования шаблонов, чтобы попытаться сопоставить все эти параметры шаблона в какой-то мегакортеж по мере формирования соединений событий и экземпляров шаблона, созданных во время компиляции, и изучить каждый мыслимый шаблон, где я каким-то образом иметь возможность раскрутить все экземпляры шаблонов, которые я создал, чтобы получить их заранее, и иметь возможность вручную очистить, но кажется, что метапрограммирование шаблонов требует новых «имен переменных» для каждой инструкции в виде typedefs или с использованием средств, которые я могу не делайте этого итеративно из того, что я вижу, но, честно говоря, область настолько сложна и легка на соответствующих примерах, что я не знаю, упустил ли я что-нибудь.
Я пытался думать о том, где еще их можно было бы хранить, но я просто не могу понять, как это могло бы быть по-другому.
Я также мог бы просто вручную вести список всех своих событий, но тогда я потерял бы одну из его более крутых и гибких функций.
ИЛИ где-то еще я могу хранить их, где я смогу получить к ним доступ. Это похоже на ситуацию уловки 22, когда само их существование требует, чтобы они находились в какой-то недоступной параллельной вселенной.
Так что да, я знаю, что есть способы решить эту проблему, не обращаясь за помощью, но все они либо приносят в жертву функцию системы, либо потенциально вызывают проблемы в будущем. Не уверен, что я когда-либо задавал здесь вопрос, но это действительно поставило меня в тупик, и я не могу найти подобную проблему у кого-то другого, большинство из них с удовольствием используют стандартную библиотеку, не имеют диспетчера памяти и помещают статические экземпляры в функции это супер удовлетворительно простой способ сделать это.
Спасибо за ваше время! Простите, если немного бессвязно, но я чувствовал, что мне нужно предоставить контекст. Я знаю, что на самом деле не так много кода, но я надеюсь, что вы поняли мою точку зрения!
Возможно, что-то в этом роде:
std::vector<std::function<void()>> cleanup_registry;
class EventSystem
{
template<typename ListenerType, typename Type>
ListenerMap<ListenerType, Type>& listenerMap()
{
static ListenerMap<ListenerType, Type> listenerMap;
static int dummy = (
cleanup_registry.push_back([&]() { listenerMap.clear(); }),
0);
return listenerMap;
}
};
void cleanup() {
for (auto& cleaner : cleanup_registry) {
cleaner();
}
std::vector<std::function<void()>>{}.swap(cleanup_registry);
}
Позвоните cleanup() прямо перед тем, как ваш игровой движок отключится.
Вы можете использовать аналогичные методы стирания типов, чтобы исключить ListenerType из своих карт, имея только одну карту для каждого типа события. Что-то вроде
template <typename Event>
using ListenerMap = eastl::unordered_map<uint32_t, std::function<void(Event)>>;
Затем dispatcher::connect (где доступен фактический тип слушателя) создаст лямбду обратного вызова для размещения на карте.
Это настолько элегантно и легко реализовать, что я три дня бился головой о стену, удивительно видеть, как это изложено таким простым способом. еще один инструмент в моем наборе инструментов, спасибо!
Черт возьми, ты красивый умный человек!