Вот несколько проблем, которые находятся на поверхности:
1. Привязка к адресу метода
В разных единицах компиляции функции и методы могут иметь разные адреса по разным причинам static, inline, специализация шаблонов и др. Проверка заняла 5 минут, ваш подход не работает.
Виртуальные методы не подерживаются.
Лямбды не поддерживаются.
Привязка методов с меньшим количеством параметров не поддерживается.
Использование std::bind невозможно.
2. Проблемы с производительностью
Многое можно перевести в constexpr.
Использование для активации сигнала virtual метода.
Лишние выделения памяти с помощью std::unique_ptr
3. Ошибки в коде
Отсутствие typename
Arg &&
Разная логика при удалении функции при активном и пассивном состоянии.
Когда встречается сочетание слов Event System первое что приходит на ум Event-driven architecture (EDA) и Event-driven programming. Ключевым моментом является наличие общего понятия Event - "сообщения, которое возникает в различных точках исполняемого кода при выполнении определённых условий". То есть Event - это не сущность вызывающая колбеки по подписке, а некоторая структура данных передаваемая между поведенческими сущностями для оповещении о любом событии, произошедшем с ними.
Структура события, как правило, состоит из двух частей. Первая содержит информацию для идентификации самого типа события, вторая - данные относительно самого факта события.
Вся прелесть EDA в том, что компоненты на столько слабо связаны, что их можно соединять в любом сочетании, так как они используют обобщенное понятие Event.
В случае, когда используются колбеки с определенным интерфейсом, как здесь, такие сущности традиционно называют сигналами, а их вызов активацией сигнала. Системы сигналов в своей сути тоже реализует паттерн Observer.
Описанное в статье не имеет ничего относящегося к Event System, а является некоторой скажем прямо не очень удачной реализацией механизма signal slot взаииодействия. О чем уже намекали в нескольких комментариях. Как реализация для изучения принципов взаимодействия подойдет, как инструмент для серьезной разработки - нет.
А если использовать в разных библиотеках не саму функцию, а только registry - он будет работать правильно.
Если передать из одной библиотеки registry сформированный в другой, то из-за несоответствия индексов в storage будет получен не тот вектор значений.
В представленном решении используется unordered_map<type_index, any>. Если использовать std::type_index или соответствующий hash_code, то таких проблем не будет.
А как же классический if constexpr? Или enable_if, или requires на сам метод test_running_time? Тогда и boid не потребовался бы. Или у этого типа есть ещё какой то смысл?
Так исторически сложилось. Такое соглашение о форматировании кода в большинстве проектов, в которых я участвовал. Похоже, Qt оказал влияние). Но всегда для удобства можно использовать псевдонимы для пространств имен.
Развиваю библиотеку "умных" оберток https://habr.com/ru/post/650701/ и для меня это нововведение весьма полезно. Сейчас код представляет собой большую портянку макросов (например, здесь https://gitlab.com/ssoft-scl/module/feature/-/blob/main/src/Detail/Operator.h), от которых теперь можно легко избавиться. Не думаю, что такой стиль будет распространён в прикладных программах, а вот инструментальные библиотеки типа std, boost и др. обязательно это будут использовать.
Если "потокобезопасность" это добавление на каждую операцию лока, то это не потокобезопасность
Средства библиотеки позволяют сделать операцию lock, как на каждую операцию отдельно, так и на любой блок кода, в зависимости от желаний разработчика. На постоянной основе сейчас можно сделать блокировку так
using Map = Wrapper< map, ThreadSafe::RecursiveMutex >;
Map map;
void foo ()
{
// в месте, где нужен постоянный lock
auto map_ptr = ↦
// map_ptr - умный указатель, который выполнил lock
if ( map_ptr->empty() )
map_ptr->doAnything();
}
Совершенно согласен с таким утверждением. Однако, из многочисленных вариантов возможной реализации данный вариант оказался наиболее компактным и совместимым с использованием обычных типов. В некоторых случаях имеется возможность реализации оператора неявного преобразования обертки к базовому типу, тогда код будет иметь привычный вид.
for ( const auto & value : values )
cout << value << endl;
Подход strong typedef к простой декларации новых типов на основе существующих известен достаточно давно и имеет свою реализацию, например, в Boost. Здесь показана возможность реализации такого подхода с помощью "умной" обертки на основе примера из доклада на коференции CppCon 2018.
И FileName и Url являются отдельными типами, а не синонимами. В этом весь смысл.
Вот несколько проблем, которые находятся на поверхности:
1. Привязка к адресу метода
В разных единицах компиляции функции и методы могут иметь разные адреса по разным причинам static, inline, специализация шаблонов и др. Проверка заняла 5 минут, ваш подход не работает.
Виртуальные методы не подерживаются.
Лямбды не поддерживаются.
Привязка методов с меньшим количеством параметров не поддерживается.
Использование std::bind невозможно.
2. Проблемы с производительностью
Многое можно перевести в constexpr.
Использование для активации сигнала virtual метода.
Лишние выделения памяти с помощью std::unique_ptr
3. Ошибки в коде
Отсутствие typename
Arg &&
Разная логика при удалении функции при активном и пассивном состоянии.
Когда встречается сочетание слов Event System первое что приходит на ум Event-driven architecture (EDA) и Event-driven programming. Ключевым моментом является наличие общего понятия Event - "сообщения, которое возникает в различных точках исполняемого кода при выполнении определённых условий". То есть Event - это не сущность вызывающая колбеки по подписке, а некоторая структура данных передаваемая между поведенческими сущностями для оповещении о любом событии, произошедшем с ними.
Структура события, как правило, состоит из двух частей. Первая содержит информацию для идентификации самого типа события, вторая - данные относительно самого факта события.
Можно использовать иерархию типов событий
Но тогда система событий будет ограничена только этой иерархией.
Либо в качестве события использовать
std::any
, тогда можно обмениваться более широким набором данных.Для реализации событийной модели используется паттерн Observer (Publisher/Subscriber)
Простейшая реализация:
Вся прелесть EDA в том, что компоненты на столько слабо связаны, что их можно соединять в любом сочетании, так как они используют обобщенное понятие Event.
В случае, когда используются колбеки с определенным интерфейсом, как здесь, такие сущности традиционно называют сигналами, а их вызов активацией сигнала. Системы сигналов в своей сути тоже реализует паттерн Observer.
По самой реализации отвечу в другом комментарии.
Описанное в статье не имеет ничего относящегося к Event System, а является некоторой скажем прямо не очень удачной реализацией механизма signal slot взаииодействия. О чем уже намекали в нескольких комментариях. Как реализация для изучения принципов взаимодействия подойдет, как инструмент для серьезной разработки - нет.
Ваше решение содержит недочеты и ошибки в реализации, исправление которых приведет к реализации @Kelbon.
Сталкивался с попдобным при использовании is_detected. Собственно интересная задача на тему ADL и правил инстанцирования шаблонов.
Если передать из одной библиотеки
registry
сформированный в другой, то из-за несоответствия индексов вstorage
будет получен не тот вектор значений.В представленном решении используется
unordered_map<type_index, any>
. Если использоватьstd::type_index
или соответствующийhash_code
, то таких проблем не будет.При такой регистрации типа возможна очень нехорошая ситуация.
При сборке разных библиотек в каждой из них будет своя собственная последовательность индексов type_index, а при сборке приложения вообще другая.
Для примера можно реализовать foolib с методом
затем barlib с методом
а затем в тестовом приложении сделать так
и получить на выходе
А как же классический if constexpr? Или enable_if, или requires на сам метод test_running_time? Тогда и boid не потребовался бы. Или у этого типа есть ещё какой то смысл?
Так исторически сложилось. Такое соглашение о форматировании кода в большинстве проектов, в которых я участвовал. Похоже, Qt оказал влияние). Но всегда для удобства можно использовать псевдонимы для пространств имен.
Развиваю библиотеку "умных" оберток https://habr.com/ru/post/650701/ и для меня это нововведение весьма полезно. Сейчас код представляет собой большую портянку макросов (например, здесь https://gitlab.com/ssoft-scl/module/feature/-/blob/main/src/Detail/Operator.h), от которых теперь можно легко избавиться.
Не думаю, что такой стиль будет распространён в прикладных программах, а вот инструментальные библиотеки типа std, boost и др. обязательно это будут использовать.
Средства библиотеки позволяют сделать операцию lock, как на каждую операцию отдельно, так и на любой блок кода, в зависимости от желаний разработчика. На постоянной основе сейчас можно сделать блокировку так
Не откладывая в долгий ящик, всё в проекте поправил. Спасибо за замечание, действительно глаз режет.
Совершенно согласен с таким утверждением. Однако, из многочисленных вариантов возможной реализации данный вариант оказался наиболее компактным и совместимым с использованием обычных типов.
В некоторых случаях имеется возможность реализации оператора неявного преобразования обертки к базовому типу, тогда код будет иметь привычный вид.
Как раз над этим сейчас и работаю).
Подход strong typedef к простой декларации новых типов на основе существующих известен достаточно давно и имеет свою реализацию, например, в Boost.
Здесь показана возможность реализации такого подхода с помощью "умной" обертки на основе примера из доклада на коференции CppCon 2018.
И FileName и Url являются отдельными типами, а не синонимами. В этом весь смысл.