Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
В Qt есть The Event System и Signals & Slots. Почему для сравнения берутся сигналы из Qt, а не сигналы из Boost?
В Qt есть The Event System и Signals & Slots.
Почему для сравнения берутся сигналы из Qt, а не сигналы из Boost?
В контексте GUI ожидается граф сцены, в ивенте содержатся координаты мыши, а ивент проходится по графу сцены.
Тогда очевидный вопрос зачем? Когда есть множество реализаций тех же сингалов?
невозможность нахождения декларации класса в .cpp-файле;Это неправда: у меня декларация находится в cpp-файле — moc генерируется! Всё работает! Можете убрать из недостатков ;)
Warning MSB8017 A circular dependency has been detected while executing custom build commands for item "GeneratedFiles\Debug\filename.moc". This may cause incremental build to work incorrectly. и все методы, которые должны быть сгенерированы moc'ом (metaObject, qt_metacast и т.д.), становятся unresolved, вызывая соответствующие ошибки. Кроме того в Generated Files, где под каждую конфигурацию должен присутствовать сгенерированный файлик moc_filename.cpp, находится только несуществующий filename.moc.C++11 это вы преуменьшаете :) std::shared_mutex ----> C++17.Действительно, размахнулся что-то.
Не собралось под GCC из-за некоторого несоответствия стандартуЭто всё моя привычка разработки под VC. Надо будет поисправлять.
У меня впечатление, что shared_ptr для Holder не обязателен и я бы попытался заменить его на unique_ptr в списке обработчиков + сырой readonly указатель во всех остальных местах.Немного не понял. В списке обработчиков как раз не Holder, а EventHandler, в котором уже Holder. Вы всё-таки при EventHandler?
лучше использовать std::make_sharedтам, где применяется не он, используется private конструктор (в Holder'ах, например, это сделано для правильной инициализации поля m_me).
Некоторые внутренние типы лучше спрятать из публичного интерфейсатак, вроде убраны же в анонимные namespace'ы.
Вы всё-таки при EventHandler?
так, вроде убраны же в анонимные namespace'ы.
Такие вещи лучше прятать в cpp файлы.А как это сделать в случае с шаблонами? Вы можете привести небольшой пример?
С шаблонными классами это невозможно (виноват, проглядел <> в IsMethodParamsCompatible), кроме ест-но переноса IsMethodParamsCompatible в cpp файл с некрасивым захардкоживанием специализаций в *.cpp файле:
// methodeventhandler.hpp
template<class TMethodHolder, class ...TParams>
class MethodEventHandler : public AbstractEventHandler<TParams...>
{
//...
virtual void call( TParams... params ) override; // прячем определение в cpp файл
// ...
};// methodeventhandler.cpp
template<class TMethodHolder, class ... TParams>
struct IsMethodParamsCompatible {
// определение класса...
};
template<class TMethodHolder, class ...TParams>
void MethodEventHandler<TMethodHolder, TParams...>::call(TParams...params)
{
// тело ф-и, скопированное из hpp
static_assert( IsMethodParamsCompatible<TMethodHolder, TParams...>::value, "Event and method arguments are not compatible" );
( m_methodHolder->m_object.*m_methodHolder->m_method )( params... );
}
// ХАРДКОД (принудительное инстанцирование заранее известного типа).
// Без кода ниже будет ошибка линковки при сборке test.cpp -
// unresolved symbol MethodEventHandler<MethodHolder<ClassHandler, int, unsigned int>, unsigned int>::call(unsigned int)
class ClassHandler;
MethodEventHandler<events::handlers::MethodHolder<ClassHandler, int, unsigned int>, unsigned int> test(nullptr);
Потенциально, вызовы трёх возможных функций — добавления, удаления и перебора (при срабатывании события) обработчиков — возможны из разных потоков в случайные моменты времени. Это создаёт целое поле возможностей по их «пересечению» во времени, «накладыванию» их исполнения друг на друга и падению программы в итоге. Попробуем избежать этого; мьютексы — наше всё.
void operator()( TParams... params )
{
m_handlerListMutex.lock_shared();
m_isCurrentItRemoved = false;
m_currentIt = m_handlers.begin();
while( m_currentIt != m_handlers.end() )
{
m_handlerListMutex.unlock_shared();
// !!!! в следующей строке может случиться все что угодно !!!!
( *m_currentIt )->call( params... );
m_handlerListMutex.lock_shared();
if( m_isCurrentItRemoved )
{
m_isCurrentItRemoved = false;
TEventHandlerIt removedIt = m_currentIt;
++m_currentIt;
deleteHandler( removedIt );
}
else
{
++m_currentIt;
}
}
m_handlerListMutex.unlock_shared();
}
Ну и нет нет никаких гарантий с точки зрения исключений в обработчикахА вот тут не совсем понятно, почему событие должно обрабатывать исключения в клиентском коде (если можно так назвать обработчики).
А вот тут не совсем понятно, почему событие должно обрабатывать исключения в клиентском коде (если можно так назвать обработчики).
void operator()( TParams... params )
{
TMyHandlerRunner newHandlerRunner( m_core );
m_core.coreMutex.lock_shared();
auto it = m_handlerRunners.insert( m_handlerRunners.end(), &newHandlerRunner );
m_core.coreMutex.unlock_shared();
//
// если исключение бросится здесь
//
newHandlerRunner.run( params... );
//
// то вот это все не отработает и в m_handlerRunners застрянет указатель на локальный newHandlerRunner
//
m_core.coreMutex.lock_shared();
m_handlerRunners.erase( it );
m_core.coreMutex.unlock_shared();
}
C++11 и обработка событий