C ++ 11和事件处理

我认为,事件处理是OOP中对象之间交互的一种方式,几乎所有曾经至少接触过OOP的人都知道这一点。 至少在我看来,这种方法在很多任务中都非常方便。 在许多编程语言中,事件处理引擎是内置的。 但是,在C ++中没有这种机制。 让我们看看您能对此做什么。

简要介绍


事件是在特定条件下某些对象可能发生的事情(例如,当您用鼠标单击该对象时使用按钮)。 其他实体可能需要意识到这一点; 然后他们订阅了该事件 。 在这种情况下,当事件发生时,将调用订阅该事件的第三方对象的处理程序 。 因此,他有机会执行一些代码,即 响应事件。 同样,如果对象不再希望响应事件,则可以取消订阅该事件 。 结果,我们有许多对象可以通过其中之一的事件以及对其他事件的反应相互连接。

这样的事情,尽管每个人都知道。

最简单的实施


实现这种行为似乎很容易。 它可能看起来像这样:

template<class ...TParams> class AbstractEventHandler { public: virtual void call( TParams... params ) = 0; protected: AbstractEventHandler() {} }; 

 template<class ...TParams> class TEvent { using TEventHandler = AbstractEventHandler<TParams...>; public: TEvent() : m_handlers() { } ~TEvent() { for( TEventHandler* oneHandler : m_handlers ) delete oneHandler; m_handlers.clear(); } void operator()( TParams... params ) { for( TEventHandler* oneHandler : m_handlers ) oneHandler->call( params... ); } void operator+=( TEventHandler& eventHandler ) { m_handlers.push_back( &eventHandler ); } private: std::list<TEventHandler*> m_handlers; }; 

 template<class TObject, class ...TParams> class MethodEventHandler : public AbstractEventHandler<TParams...> { using TMethod = void( TObject::* )( TParams... ); public: MethodEventHandler( TObject& object, TMethod method ) : AbstractEventHandler<TParams...>(), m_object( object ), m_method( method ) { assert( m_method != nullptr ); } virtual void call( TParams... params ) override final { ( m_object.*m_method )( params... ); } private: TObject& m_object; TMethod m_method; }; template<class TObject, class ...TParams> AbstractEventHandler<TParams...>& createMethodEventHandler( TObject& object, void( TObject::*method )( TParams... ) ) { return *new MethodEventHandler<TObject, TParams...>( object, method ); } #define METHOD_HANDLER( Object, Method ) createMethodEventHandler( Object, &Method ) #define MY_METHOD_HANDLER( Method ) METHOD_HANDLER( *this, Method ) 

这种情况的申请应采用以下形式:

 class TestWindow { . . . public: TEvent<const std::string&, unsigned int> onButtonClick; . . . }; class ClickEventHandler { . . . public: void testWindowButtonClick( const std::string&, unsigned int ) { ... } . . . }; int main( int argc, char *argv[] ) { . . . TestWindow testWindow; ClickEventHandler clickEventHandler; testWindow.onButtonClick += METHOD_HANDLER( clickEventHandler, ClickEventHandler::testWindowButtonClick ); . . . } 

自然,处理程序方法(类的函数成员)将不是唯一的处理程序类型,但稍后会介绍更多。

一切似乎都很方便,紧凑而且很棒。 但是尽管有很多缺点。

处理程序比较


为了实现事件的退订,有必要将比较的可能性添加到处理程序中(通过==!== )。 那些调用相同对象(即相同类的相同实例)的相同方法(一个类的成员函数)的处理程序将被视为相等。

 template<class ...TParams> class AbstractEventHandler { . . . using MyType = AbstractEventHandler<TParams...>; public: bool operator==( const MyType& other ) const { return isEquals( other ); } bool operator!=( const MyType& other ) const { return !( *this == other ); } protected: virtual bool isEquals( const MyType& other ) const = 0; . . . }; 

 template<class TMethodHolder, class ...TParams> class MethodEventHandler : public AbstractEventHandler<TParams...> { . . . using TMethod = void( TObject::* )( TParams... ); protected: virtual bool isEquals( const AbstractEventHandler<TParams...>& other ) const override { const MyType* _other = dynamic_cast<const MyType*>( &other ); return ( _other != nullptr && &m_object == &_other.m_object && m_method == _other.m_method ); } private: TObject& m_object; TMethod m_method; . . . }; 

然后,我们将能够从事件订阅中删除处理程序。 在这种情况下,有必要禁止添加相同(相等)的处理程序。

 template<class ...TParams> class TEvent { . . . using TEventHandler = AbstractEventHandler<TParams...>; using TEventHandlerIt = typename std::list<TEventHandler*>::const_iterator; public: bool operator+=( TEventHandler& eventHandler ) { if( findEventHandler( eventHandler ) == m_handlers.end() ) { m_handlers.push_back( &eventHandler ); return true; } return false; } bool operator-=( TEventHandler& eventHandler ) { auto it = findEventHandler( eventHandler ); if( it != m_handlers.end() ) { TEventHandler* removedEventHandler = *it; m_handlers.erase( it ); delete removedEventHandler; return true; } return false; } private: inline TEventHandlerIt findEventHandler( TEventHandler& eventHandler ) const { return std::find_if( m_handlers.cbegin(), m_handlers.cend(), [ &eventHandler ]( const TEventHandler* oneHandler ) { return ( *oneHandler == eventHandler ); } ); } std::list<TEventHandler*> m_handlers; . . . }; 

在此,处理程序的添加/删除功能如果成功则返回true,如果未执行相应的操作(添加或删除),则返回false

是的,具有比较的用例涉及创建临时的,无处添加的处理程序,该处理程序不会在任何地方删除。 但是稍后会更多。

可以使用吗? 尚未完全实施。

在处理程序中删除处理程序


因此,我们在代码执行过程中立即遇到崩溃,处理程序将自己取消订阅该事件(我认为这不是处理程序在任何情况下自动剪切的最罕见的用例 ):

 class TestWindow { . . . public: TEvent<const std::string&, unsigned int> onButtonClick; static TestWindow& instance(); . . . }; class ClickEventHandler { . . . public: void testWindowButtonClick( const std::string&, unsigned int ) { TestWindow::instance().onButtonClick -= MY_METHOD_HANDLER( ClickEventHandler::testWindowButtonClick ); } . . . }; int main( int argc, char *argv[] ) { . . . ClickEventHandler clickEventHandler; TestWindow::instance().onButtonClick += METHOD_HANDLER( clickEventHandler, ClickEventHandler::testWindowButtonClick ); . . . } 

出现问题的原因很简单:

  • 事件被触发并遍历(使用迭代器)处理程序,并调用它们;
  • 自身中的另一个处理程序导致自身被删除;
  • 事件删除给定的处理程序,使相应的迭代器无效;
  • 在完成此处理程序之后,该事件将返回枚举其他事件,但是,当前的迭代器(对应于远程处理程序)已经无效;
  • 事件正在尝试访问无效的迭代器,从而导致失败。

因此,有必要检查何时可以更改处理程序列表的情况,这将导致迭代器无效; 然后为此类迭代器实施读取保护。

std :: list '在此应用程序中的优点是,在删除它时,只会使一个迭代器无效-在删除的元素上(例如,影响以下内容); 并且添加元素根本不会导致任何迭代器失效。 因此,我们需要控制唯一的情况:在元素的当前枚举中删除迭代器为最新的元素。 在这种情况下,例如,您不能删除元素,而只是标记要删除当前元素,然后在元素枚举中完成它。

可以立即实施该解决方案,但是我建议通过以下方法解决此问题。

线程安全


潜在地,可以在不同的时间从不同的线程调用三个可能的函数-添加,删除和排序(触发事件时)处理程序。 这为它们的时间“交集”,它们的执行彼此“重叠”以及程序的失败创造了一个广阔的可能性领域。 让我们尝试避免这种情况; 互斥锁是我们的一切

 template<class ...TParams> class TEvent { using TEventHandler = AbstractEventHandler<TParams...>; using TEventHandlerIt = typename std::list<TEventHandler*>::const_iterator; public: TEvent() : m_handlers(), m_currentIt(), m_isCurrentItRemoved( false ), m_handlerListMutex() { } 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(); } bool operator+=( TEventHandler& eventHandler ) { std::unique_lock<std::shared_mutex> _handlerListMutexLock( m_handlerListMutex ); if( findEventHandler( eventHandler ) == m_handlers.end() ) { m_handlers.push_back( std::move( eventHandler ) ); return true; } return false; } bool operator-=( TEventHandler& eventHandler ) { std::unique_lock<std::shared_mutex> _handlerListMutexLock( m_handlerListMutex ); auto it = findEventHandler( eventHandler ); if( it != m_handlers.end() ) { if( it == m_currentIt ) m_isCurrentItRemoved = true; else deleteHandler( it ); return true; } return false; } private: //      'm_handlerListMutex' inline TEventHandlerIt findEventHandler( TEventHandler& eventHandler ) const { return std::find_if( m_handlers.cbegin(), m_handlers.cend(), [ &eventHandler ]( const TEventHandler* oneHandler ) { return ( *oneHandler == eventHandler ); } ); } //      'm_handlerListMutex' inline void deleteHandler( TEventHandlerIt it ) { TEventHandler* removedEventHandler = *it; m_handlers.erase( it ); delete removedEventHandler; } std::list<TEventHandler*> m_handlers; //    'm_handlerListMutex' mutable TEventHandlerIt m_currentIt; mutable bool m_isCurrentItRemoved; mutable std::shared_mutex m_handlerListMutex; }; 

几乎每次调用每个处理程序时,都不要忘记让窗口保持打开状态。 这是必需的,以便可以在处理程序内部访问事件并对其进行更改(例如,添加/删除处理程序)而不会引起死锁 。 您不必担心数据的有效性,因为,正如我们所发现的,导致这种情况的唯一原因就是删除了当前元素,并且这种情况已经得到处理。
UPD1。 感谢Cheater建议 std :: shared_mutex仅出现在C ++ 17中 (而std :: shared_lock仅出现在C ++ 14中 )。 那些对此至关重要的人可能与std :: mutex有关
UPD2。 此外,关于线程安全性 (不保留旁白的顺序)。

事件可见性问题


当将事件用作类的成员时,将其设为public似乎合乎逻辑,以便第三方对象可以添加/删除其处理程序。 但是,这将导致运算符() ,即 事件调用也可以从外部访问,在某些情况下可能无法接受。 我们将通过与事件类( TEvent <...> )隔离仅用于处理处理程序的抽象接口来解决此问题。

 template<class ...TParams> class IEvent { protected: using TEventHandler = AbstractEventHandler<TParams...>; public: bool operator+=( TEventHandler& eventHandler ) { return addHandler( eventHandler ); } bool operator-=( TEventHandler& eventHandler ) { return removeHandler( eventHandler ); } protected: IEvent() {} virtual bool addHandler( TEventHandler& eventHandler ) = 0; virtual bool removeHandler( TEventHandler& eventHandler ) = 0; }; 

 template<class ...TParams> class TEvent : public IEvent<TParams...> { . . . public: TEvent() : IEvent<TParams...>() . . . { } protected: virtual bool addHandler( TEventHandler& eventHandler ) override { // ,     'TEvent::operator+=' } virtual bool removeHandler( TEventHandler& eventHandler ) override { // ,     'TEvent::operator-=' } . . . }; 

现在,我们可以将事件的负责处理程序的部分和负责调用处理程序的部分划分为不同的范围。

 class TestWindow { . . . public: TestWindow() : onButtonClick( m_onButtonClick ), m_onButtonClick() { } IEvent<const std::string&, unsigned int>& onButtonClick; protected: TEvent<const std::string&, unsigned int> m_onButtonClick; . . . }; 

因此,现在第三方对象可以通过TestWindow :: onButtonClick添加/删除其处理程序,但是它们本身将无法触发此事件。 现在只能在TestWindow类(及其事件,例如,事件的范围受保护 )中进行调用。

琐碎的代码正在慢慢开始变成巨大的东西,但这还不是结束。

匹配事件参数及其处理程序


在当前实现中,事件及其任何处理程序必须具有严格对应的参数列表。 这导致许多缺点。

第一个。 假设我们有一个类模板,其中有一个带有模板参数的事件。

 template<class TSource> class MyClass { . . . public: TEvent<const TSource&> onValueChanged; . . . }; 

由于事先不知道将要使用的类型,因此通过常量链接而不是通过值来传递它是有意义的。 但是,现在对于任何实现,即使具有基本类型,都必须存在相应的处理程序。

 MyClass<bool> myBoolClass; . . . template<class TSource> class MyHandlerClass { . . . private: void handleValueChanged1( const bool& newValue ); void handleValueChanged2( bool newValue ); . . . }; . . . MyHandlerClass myHandlerClass; myBoolClass.onValueChanged += METHOD_HANDLER( myHandlerClass, MyHandlerClass::handleValueChanged1 ); // OK myBoolClass.onValueChanged += METHOD_HANDLER( myHandlerClass, MyHandlerClass::handleValueChanged2 ); // compile error 

我希望能够将MyHandlerClass :: handleValueChanged2形式的处理程序连接到类似事件,但是到目前为止,没有这种可能性。

第二个。 让我们尝试实现类似于现有处理程序方法(类的-function-member)的仿函数处理程序。

 template<class TFunctor, class ...TParams> class FunctorEventHandler : public AbstractEventHandler<TParams...> { public: FunctorEventHandler( TFunctor& functor ) : AbstractEventHandler<TParams...>(), m_functor( functor ) { } virtual void call( TParams... params ) override final { m_functor( params... ); } private: TFunctor& m_functor; }; template<class TFunctor, class ...TParams> AbstractEventHandler<TParams...>& createFunctorEventHandler( TFunctor&& functor ) { return *new FunctorEventHandler<TFunctor, TParams...>( functor ); } #define FUNCTOR_HANDLER( Functor ) createFunctorEventHandler( Functor ) 

现在尝试将其固定在某个事件上。

 class TestWindow { . . . public: TEvent<const std::string&, unsigned int> onButtonClick; . . . }; struct ClickEventHandler { void operator()( const std::string&, unsigned int ) { . . . } }; int main( int argc, char *argv[] ) { . . . TestWindow testWindow; ClickEventHandler clickEventHandler; testWindow.onButtonClick += FUNCTOR_HANDLER( clickEventHandler ); . . . } 

结果将是编译错误。 对于createFunctorEventHandler函数编译器无法从函数的唯一参数( 仿函数本身)推断TParams ...的类型。 该函子实际上不包含有关根据其创建哪种类型的处理程序的任何信息。 在这种情况下,唯一可以做的事情就是编写如下内容:

 testWindow.onButtonClick += createFunctorEventHandler<ClickEventHandler, const std::string&, unsigned int>( clickEventHandler ); 

但是您根本不想这样做。

将事件连接到各种类型的处理程序


因此,有一个愿望清单,取决于实施情况。 我们将以函子处理程序为例来考虑这种情况;将以类似的方式获得处理程序方法(类的函数成员)。

由于仅凭函子就无法说出相应处理程序的参数列表是什么,因此我们不会这样做。 这个问题不是在创建处理程序时而是在尝试将其附加到特定事件时变得相关。 是的,这是两个不同的观点。 这个想法可以实现如下:

 template<class TFunctor> class FunctorHolder; template<class TFunctor, class ...TParams> class FunctorEventHandler : public AbstractEventHandler<TParams...> { public: FunctorEventHandler( FunctorHolder<TFunctor>& functorHolder ) : AbstractEventHandler<TParams...>(), m_functorHolder( functorHolder ) { } virtual void call( TParams... params ) override { m_functorHolder.m_functor( params... ); } private: FunctorHolder<TFunctor>& m_functorHolder; . . . }; 

 template<class TFunctor> class FunctorHolder { public: FunctorHolder( TFunctor& functor ) : m_functor( functor ) { } template<class ...TCallParams> operator AbstractEventHandler<TCallParams...>&() { return *new FunctorEventHandler<TFunctor, TCallParams...>( *this ); } private: TFunctor& m_functor; . . . template<class TFunctor, class ...TParams> friend class FunctorEventHandler; }; 

 template<class TFunctor> FunctorHolder<TFunctor>& createFunctorEventHandler( TFunctor&& functor ) { return *new FunctorHolder<TFunctor>( functor ); } #define FUNCTOR_HANDLER( Functor ) createFunctorEventHandler( Functor ) #define LAMBDA_HANDLER( Lambda ) FUNCTOR_HANDLER( Lambda ) #define STD_FUNCTION_HANDLER( StdFunction ) FUNCTOR_HANDLER( StdFunction ) #define FUNCTION_HANDLER( Function ) FUNCTOR_HANDLER( &Function ) 

 template<class ...TParams> class IEvent { protected: using TEventHandler = AbstractEventHandler<TParams...>; public: template<class TSome> bool operator+=( TSome&& some ) { return addHandler( static_cast<TEventHandler&>( some ) ); } template<class TSome> bool operator-=( TSome&& some ) { return removeHandler( static_cast<TEventHandler&>( some ) ); } protected: IEvent() {} virtual bool addHandler( TEventHandler& eventHandler ) = 0; virtual bool removeHandler( TEventHandler& eventHandler ) = 0; }; 

简而言之,处理程序创建时刻与事件附加事件之间的分离比以前更加明显。 这避免了上一段中描述的问题。 当尝试将特定的FunctorHolder附加到特定的FunctorEventHandler时 ,或者使用非常特定的仿函数类型创建FunctorEventHandler <...>类的实例时,将进行类型兼容性测试。 在这个类中,将有一行代码m_functorHolder.m_functor(params ...); ,它根本无法针对与函子(或根本不是函子,即不具有operator()的对象)不兼容的一组类型进行编译。

我重复一遍,下面将讨论删除临时对象的问题。 另外,值得注意的是,每种情况下都会生成一堆宏,首先是为了演示这种类型的处理程序的功能,其次是在可能通过文件修改其中一个宏的情况下。

检查结果。

 class TestWindow { . . . public: TEvent<const std::string&, unsigned int> onButtonClick; . . . }; struct Functor { void operator()( const std::string&, unsigned int ) {} }; struct Functor2 { void operator()( std::string, unsigned int ) {} }; struct Functor3 { void operator()( const std::string&, const unsigned int& ) {} }; struct Functor4 { void operator()( std::string, const unsigned int& ) {} }; struct Functor5 { void operator()( std::string&, unsigned int& ) {} }; struct Functor6 { void operator()( const std::string&, unsigned int& ) {} }; struct Functor7 { void operator()( std::string&, const unsigned int& ) {} }; int main( int argc, char *argv[] ) { . . . TestWindow testWindow; Functor functor; Functor2 functor2; Functor3 functor3; Functor4 functor4; Functor5 functor5; Functor6 functor6; Functor7 functor7; testWindow.onButtonClick += FUNCTOR_HANDLER( functor ); // ok testWindow.onButtonClick += FUNCTOR_HANDLER( functor2 ); // ok testWindow.onButtonClick += FUNCTOR_HANDLER( functor3 ); // ok testWindow.onButtonClick += FUNCTOR_HANDLER( functor4 ); // ok testWindow.onButtonClick += FUNCTOR_HANDLER( functor5 ); // compile error testWindow.onButtonClick += FUNCTOR_HANDLER( functor6 ); // ok testWindow.onButtonClick += FUNCTOR_HANDLER( functor7 ); // compile error . . . } 

尝试将参数之一从const lvalue转换为lvalue时,发生编译错误。 从rvalue转换为unconst lvalue不会导致错误,尽管值得注意的是,这会在腿上造成自拍的潜在威胁:处理程序将能够更改复制到堆栈上的变量,当该处理程序退出时,将很高兴地删除该变量。

通常,错误消息应如下所示:

 Error C2664 'void Functor5::operator ()(std::string &,unsigned int &)': cannot convert argument 1 from 'const std::string' to 'std::string &' 

为了更加清晰,在第三方代码中使用事件和处理程序时,可以添加自己的错误消息。 这将需要编写一个小的支持结构(我承认,我在某处发现了类似的方法):

 namespace { template<class TFunctor, class ...TParams> struct IsFunctorParamsCompatible { private: template<class TCheckedFunctor, class ...TCheckedParams> static constexpr std::true_type exists( decltype( std::declval<TCheckedFunctor>()( std::declval<TCheckedParams>()... ) )* = nullptr ); template<class TCheckedFunctor, class ...TCheckedParams> static constexpr std::false_type exists( ... ); public: static constexpr bool value = decltype( exists<TFunctor, TParams...>( nullptr ) )::value; }; } // 

 template<class TFunctor, class ...TParams> class FunctorEventHandler : public AbstractEventHandler<TParams...> { . . . public: virtual void call( TParams... params ) override { static_assert( IsFunctorParamsCompatible<TFunctor, TParams...>::value, "Event and functor arguments are not compatible" ); m_functorHolder->m_functor( params... ); } . . . }; 

这项工作基于SFINAE机制。 简而言之,尝试编译存在的第一个函数,但是,如果由于参数不兼容(或作为函子传递的函数缺少操作符() )而导致编译第一个函数不起作用,则编译器不会抛出错误,而只是尝试编译第二个函数; 我们会尽一切努力使其编译始终成功,然后根据编译了哪个函数的事实,得出结论(通过将结果写入value )得出给定类型的参数的兼容性。

现在,错误消息将如下所示:

 Error C2338 Event and functor arguments are not compatible Error C2664 'void Functor5::operator ()(std::string &,unsigned int &)': cannot convert argument 1 from 'const std::string' to 'std::string &' 

除了附加的,信息量更大的错误消息外,此方法还解决了将参数从rvalue转换为unconst lvalue的问题 :现在它会导致参数不兼容错误,即 尝试从上面的示例添加functor6处理程序会导致编译时错误。
UPD 提炼 (不保留叙述顺序)

函子比较


由于处理程序类的更改,比较此类的实例的实现将稍有更改。 再次,我将仅提供函子处理程序的实现,因为处理程序方法(类的-function-member)将看起来相似。

 template<class ...TParams> class AbstractEventHandler { . . . using MyType = AbstractEventHandler<TParams...>; public: bool operator==( const MyType& other ) const { return isEquals( other ); } bool operator!=( const MyType& other ) const { return !( *this == other ); } protected: virtual bool isEquals( const MyType& other ) const = 0; . . . }; 

 template<class TFunctor, class ...TParams> class FunctorEventHandler : public AbstractEventHandler<TParams...> { . . . using MyType = FunctorEventHandler<TFunctor, TParams...>; protected: virtual bool isEquals( const AbstractEventHandler<TParams...>& other ) const override { const MyType* _other = dynamic_cast<const MyType*>( &other ); return ( _other != nullptr && *m_functorHolder == *_other->m_functorHolder ); } private: FunctorHolder<TFunctor>& m_functorHolder; . . . }; 

 template<class TFunctor> class FunctorHolder { . . . using MyType = FunctorHolder<TFunctor>; public: bool operator==( const MyType& other ) const { return ( m_functor == other.m_functor ); } bool operator!=( const MyType& other ) const { return !( *this == other ); } private: TFunctor& m_functor; . . . }; 

在此,比较结束和实现部分的相似之处仅针对函子处理程序开始。

如上所述,我们已经获得了几种类型的函子处理程序:直接函子对象,lambda表达式, std :: function类的实例,单个函数。 其中,函子对象,lambda表达式和std ::函数类的实例无法使用运算符==进行比较(它们需要在地址处进行比较),但是各个函数可以进行比较,因为 已经存储在。 为了不针对每种情况分别重写比较函数,我们以一般形式编写它:

 namespace { template<class TEqu, class TEnabled = void> struct EqualityChecker; template<class TEquatable> struct EqualityChecker<TEquatable, typename std::enable_if<is_equatable<TEquatable>::value>::type> { static constexpr bool isEquals( const TEquatable& operand1, const TEquatable& operand2 ) { return ( operand1 == operand2 ); } }; template<class TNonEquatable> struct EqualityChecker<TNonEquatable, typename std::enable_if<!is_equatable<TNonEquatable>::value>::type> { static constexpr bool isEquals( const TNonEquatable& operand1, const TNonEquatable& operand2 ) { return ( &operand1 == &operand2 ); } }; } // template<class TFunctor> class FunctorHolder { . . . using MyType = FunctorHolder<TFunctor>; public: bool operator==( const MyType& other ) const { return EqualityChecker<TFunctor>::isEquals( m_functor, other.m_functor ); } private: TFunctor& m_functor; . . . }; 

可以理解,is_equatable是一个辅助模板,它确定是否可以检查给定类型的两个实例是否相等。std :: enable_if的帮助下,我们选择了两个部分专用的EqualityChecker结构之一,该结构将进行比较:按值或按地址。Is_equatable实现可以如下:

 template<class T> class is_equatable { private: template<class U> static constexpr std::true_type exists( decltype( std::declval<U>() == std::declval<U>() )* = nullptr ); template<class U> static constexpr std::false_type exists( ... ); public: static constexpr bool value = decltype( exists<T>( nullptr ) )::value; }; 

此实现基于先前已使用SFINAE机制仅在这里,我们检查给定类实例的操作符==的存在通过这种简单的方法,就可以完成处理程序-函数比较的实现。



垃圾收集


宽容,我也想插入一个大标题。

我们正在接近尾声,现在该摆脱那些没人能控制的庞大创建对象了。

每个带有处理程序的事件操作都会创建两个对象:Holder(用于存储处理程序的可执行部分)和EventHandler将其与事件关联。我们不会忘记,在尝试重新添加处理程序的情况下,不会发生任何添加-两个对象“悬空了”(当然,除非每次都单独检查此情况)。另一种情况:删除处理程序;还创建了两个新对象,以在事件处理程序列表中搜索相同(相等)的对象;从列表中找到的处理程序当然会被删除(如果有的话),而为搜索而创建的由两个对象组成的临时处理程序又“悬而未决”。一般来说,不酷。

转向智能指针。您需要确定两个处理程序对象的每个所有权的语义是:唯一所有权(std :: unique_ptr)或共享(std :: shared_ptr)。

持有人,除了在添加/删除事件时由事件本身使用之外,还应将其存储在EventHandler中,因此我们使用共享所有权,对于EventHandler来说是唯一的,因为创建后,它将仅存储在事件处理程序列表中。

我们意识到这个想法:

 template<class ...TParams> class AbstractEventHandler { . . . public: virtual ~AbstractEventHandler() {} . . . }; template<class ...Types> using THandlerPtr = std::unique_ptr<AbstractEventHandler<Types...>>; 

 namespace { template<class TSome> struct HandlerCast { template<class ...Types> static constexpr THandlerPtr<Types...> cast( TSome& some ) { return static_cast<THandlerPtr<Types...>>( some ); } }; template<class TPtr> struct HandlerCast<std::shared_ptr<TPtr>> { template<class ...Types> static constexpr THandlerPtr<Types...> cast( std::shared_ptr<TPtr> some ) { return HandlerCast<TPtr>::cast<Types...>( *some ); } }; } // template<class ...TParams> class IEvent { public: template<class TSome> bool operator+=( TSome&& some ) { return addHandler( HandlerCast<TSome>::cast<TParams...>( some ) ); } template<class TSome> bool operator-=( TSome&& some ) { return removeHandler( HandlerCast<TSome>::cast<TParams...>( some ) ); } protected: using TEventHandlerPtr = THandlerPtr<TParams...>; IEvent() {} virtual bool addHandler( TEventHandlerPtr eventHandler ) = 0; virtual bool removeHandler( TEventHandlerPtr eventHandler ) = 0; }; template<class ...TParams> class TEvent : public IEvent<TParams...> { using TEventHandlerIt = typename std::list<TEventHandlerPtr>::const_iterator; public: TEvent() { . . . } ~TEvent() { // empty } protected: virtual bool addHandler( TEventHandlerPtr eventHandler ) override { std::unique_lock<std::shared_mutex> _handlerListMutexLock( m_handlerListMutex ); if( findEventHandler( eventHandler ) == m_handlers.end() ) { m_handlers.push_back( std::move( eventHandler ) ); return true; } return false; } virtual bool removeHandler( TEventHandlerPtr eventHandler ) override { . . . } private: //      'm_handlerListMutex' inline TEventHandlerIt findEventHandler( const TEventHandlerPtr& eventHandler ) const { return std::find_if( m_handlers.cbegin(), m_handlers.cend(), [ &eventHandler ]( const TEventHandlerPtr& oneHandler ) { return ( *oneHandler == *eventHandler ); } ); } //      'm_handlerListMutex' inline void deleteHandler( TEventHandlerIt it ) { m_handlers.erase( it ); } std::list<TEventHandlerPtr> m_handlers; . . . }; 

 template<class TMethodHolder, class ...TParams> class MethodEventHandler : public AbstractEventHandler<TParams...> { . . . using TMethodHolderPtr = std::shared_ptr<TMethodHolder>; public: MethodEventHandler( TMethodHolderPtr methodHolder ) : AbstractEventHandler<TParams...>(), m_methodHolder( methodHolder ) { assert( m_methodHolder != nullptr ); } private: TMethodHolderPtr m_methodHolder; . . . }; template<class TObject, class ...TParams> class MethodHolder { using MyType = MethodHolder<TObject, TParams...>; using TMethod = void( TObject::* )( TParams... ); public: MethodHolder( TObject& object, TMethod method ) { . . . } template<class ...TCallParams> operator THandlerPtr<TCallParams...>() { return THandlerPtr<TCallParams...>( new MethodEventHandler<MyType, TCallParams...>( /*   ? */ ) ); } . . . }; template<class TObject, class ...TParams> std::shared_ptr<MethodHolder<TObject, TParams...>> createMethodEventHandler( TObject& object, void( TObject::*method )( TParams... ) ) { return std::shared_ptr<MethodHolder<TObject, TParams...>>( new MethodHolder<TObject, TParams...>( object, method ) ); } #define METHOD_HANDLER( Object, Method ) createMethodEventHandler( Object, &Method ) #define MY_METHOD_HANDLER( Method ) METHOD_HANDLER( *this, Method ) 

首先是第一件事。

首先,该事件及其用于处理程序的接口。在后者中,您不再可以使用static_cast直接转换类型,因为要转换的类型位于std :: shared_ptr内部。现在,对于这种转换,我们将使用辅助HandlerCast结构,该结构通过其私有的专用化,将提供对std :: shared_ptr内部对象的访问,并且已经使用该结构(在其非专用实现中),它将应用良好的旧static_cast

事件本身;这里也有几个重要的变化。首先,我们将停止在析构函数中以及在删除时手动删除处理程序实例;现在,只需将具有此处理程序的智能指针从列表中删除即可。另外,在添加处理程序时,重要的是不要忘记std :: move,因为std :: unique_ptr不支持复制(对于这种语义这是很合逻辑的)。

让我们继续处理程序。根据旧的传统,只给出其中一个,第二个相似。乍一看,这全都归结为将存储/创建的对象的类型从链接/指针更改为智能指针。

但是有一个微妙的地方。createMethodEventHandler函数std :: shared_ptr返回到实例MethodHolder。稍后,将尝试将其转换为处理程序类型(MethodEventHandler),在该处理程序中必须创建一个新的MethodEventHandler实例,并将其自身传递给构造函数std :: shared_ptr。这恰好是稍后删除MethodEventHandler实例时要删除MethodHolder实例的目的。但是问题在于,MethodHolder无法访问已创建的std :: shared_ptr,该std :: shared_ptr本身存储了它。

要解决此问题,您必须在MethodHolder中存储指向自己智能指针。但是,为了不影响其移除,我们使用std :: weak_ptr

 template<class TObject, class ...TParams> class MethodHolder { using MyType = MethodHolder<TObject, TParams...>; using TMethod = void( TObject::* )( TParams... ); public: template<class ...TCallParams> operator THandlerPtr<TCallParams...>() { return THandlerPtr<TCallParams...>( new MethodEventHandler<MyType, TCallParams...>( m_me.lock() ) ); } template<class TObject, class ...TParams> static std::shared_ptr<MyType> create( TObject& object, TMethod method ) { std::shared_ptr<MyType> result( new MyType( object, method ) ); result->m_me = result; return result; } private: MethodHolder( TObject& object, TMethod method ) : m_object( object ), m_method( method ) { assert( m_method != nullptr ); } TObject& m_object; TMethod m_method; std::weak_ptr<MyType> m_me; }; template<class TObject, class ...TParams> std::shared_ptr<MethodHolder<TObject, TParams...>> createMethodEventHandler( TObject& object, void( TObject::*method )( TParams... ) ) { return MethodHolder<TObject, TParams...>::create( object, method ); } 

为了清楚起见,从事件中删除处理程序时,我将给出事件的大致顺序(对随机双关语表示歉意):

  • 事件从列表中删除该项目(m_handlers.erase(it);),这将导致其析构函数被调用;
  • 调用析构函数std :: unique_ptr,导致对托管对象的析构函数的调用;
  • 调用MethodEventHandler析构函数,该方法将删除对象的所有字段,包括m_methodHolder字段,即std :: shared_ptr
  • std::shared_ptr ; , (.. ) ( MethodHolder ); , std::weak_ptr ;
  • MethodHolder , , , m_me , std::weak_ptr ;
  • std::weak_ptr ; ; .. std::weak_ptr , ;
  • .

重要的是要记住,AbstractEventHandler类的析构函数必须是虚拟的。否则,在第3节的2节之后,将调用AbstractEventHandler析构函数,并且将不执行其他操作。

事件和处理程序连接


在某些情况下,根据事件的发生(根据某种逻辑)添加/删除事件的处理程序非常频繁时,您不希望每次都获取事件的实例和处理程序的实例,而再次从该事件中实现订阅/取消订阅。但是我想将它们连接一次,然后,如有必要,使用此连接,从预定义事件中添加/删除预定义处理程序。您可以如下实现:

 template<class ...Types> using THandlerPtr = std::shared_ptr<AbstractEventHandler<Types...>>; 

 template<class ...TParams> class IEvent { . . . protected: using TEventHandlerPtr = THandlerPtr<TParams...>; virtual bool isHandlerAdded( const TEventHandlerPtr& eventHandler ) const = 0; virtual bool addHandler( TEventHandlerPtr eventHandler ) = 0; virtual bool removeHandler( TEventHandlerPtr eventHandler ) = 0; friend class HandlerEventJoin<TParams...>; . . . }; template<class ...TParams> class TEvent : public IEvent<TParams...> { . . . protected: virtual bool isHandlerAdded( const TEventHandlerPtr& eventHandler ) const override { std::shared_lock<std::shared_mutex> _handlerListMutexLock( m_handlerListMutex ); return ( findEventHandler( eventHandler ) != m_handlers.end() ); } virtual bool addHandler( TEventHandlerPtr eventHandler ) override { . . . } virtual bool removeHandler( TEventHandlerPtr eventHandler ) override { . . . } private: //      'm_handlerListMutex' inline TEventHandlerIt findEventHandler( const TEventHandlerPtr& eventHandler ) const { . . . } std::list<TEventHandlerPtr> m_handlers; mutable std::shared_mutex m_handlerListMutex; . . . }; 

 template<class ...TParams> class HandlerEventJoin { public: HandlerEventJoin( IEvent<TParams...>& _event, THandlerPtr<TParams...> handler ) : m_event( _event ), m_handler( handler ) { } inline bool isJoined() const { return m_event.isHandlerAdded( m_handler ); } inline bool join() { return m_event.addHandler( m_handler ); } inline bool unjoin() { return m_event.removeHandler( m_handler ); } private: IEvent<TParams...>& m_event; THandlerPtr<TParams...> m_handler; }; 

如您所见,现在已经添加了另一个存储处理程序实例的可能位置,因此我们将为此使用std :: shared_ptr而不是std :: unique_ptr

但是,对于我来说,该类使用起来有点不方便。我想存储和创建连接实例,而没有实例化类模板的参数列表。

我们使用抽象祖先类和包装器来实现此目的:

 class AbstractEventJoin { public: virtual ~AbstractEventJoin() {} virtual bool isJoined() const = 0; virtual bool join() = 0; virtual bool unjoin() = 0; protected: AbstractEventJoin() {} }; 

 template<class ...TParams> class HandlerEventJoin : public AbstractEventJoin { . . . public: virtual inline bool isJoined() const override { . . . } virtual inline bool join() override { . . . } virtual inline bool unjoin() override { . . . } . . . }; 

 class EventJoinWrapper { public: template<class TSome, class ...TParams> inline EventJoinWrapper( IEvent<TParams...>& _event, TSome&& handler ) : m_eventJoin( std::make_shared<HandlerEventJoin<TParams...>>( _event, HandlerCast<TSome>::cast<TParams...>( handler ) ) ) { } constexpr EventJoinWrapper() : m_eventJoin( nullptr ) { } ~EventJoinWrapper() { if( m_eventJoin != nullptr ) delete m_eventJoin; } operator bool() const { return isJoined(); } bool isAssigned() const { return ( m_eventJoin != nullptr ); } bool isJoined() const { return ( m_eventJoin != nullptr && m_eventJoin->isJoined() ); } bool join() { return ( m_eventJoin != nullptr ? m_eventJoin->join() : false ); } bool unjoin() { return ( m_eventJoin != nullptr ? m_eventJoin->unjoin() : false ); } private: AbstractEventJoin* m_eventJoin; }; using EventJoin = EventJoinWrapper; 

HandlerCast此处使用的支持结构相同。顺便说一句,重要的是不要忘记将AbstractEventJoin析构函数设为虚拟,以便在EventJoinWrapper析构函数中删除其实例时,将调用HandlerEventJoin析构函数,否则将调用THandlerPtr字段,因此不会破坏处理程序本身。

这种实现似乎是可行的,但是乍一看。复制或移动EventJoinWrapper的实例将再次在其析构函数中删除m_eventJoin。因此,我们使用std :: shared_ptr来存储实例AbstractEventJoin,并实现了稍微优化的移动(和复制)语义,因为 这将是一个潜在的频繁操作。

 class EventJoinWrapper { public: EventJoinWrapper( EventJoinWrapper&& other ) : m_eventJoin( std::move( other.m_eventJoin ) ) { } EventJoinWrapper( EventJoinWrapper& other ) : m_eventJoin( other.m_eventJoin ) { } ~EventJoinWrapper() { /*empty*/ } EventJoinWrapper& operator=( EventJoinWrapper&& other ) { m_eventJoin = std::move( other.m_eventJoin ); return *this; } EventJoinWrapper& operator=( const EventJoinWrapper& other ) { m_eventJoin = other.m_eventJoin; return *this; } . . . private: std::shared_ptr<AbstractEventJoin> m_eventJoin; }; 

现在,将处理程序连接到事件时,您可以立即返回新连接的实例:

 template<class ...TParams> class IEvent { . . . public: template<class TSome> EventJoin operator+=( TSome&& some ) { EventJoin result( *this, std::forward<TSome>( some ) ); result.join(); return result; } . . . }; 

并且在通过include (IEvent <= EventJointWrapper.hpp; EventJointWrapper <= HandlerEventJoin.hpp; HandlerEventJoin <= IEvent.hpp)解决了三角形依赖性之后,可以将某些文件分为.h.hpp,您甚至可以使用此文件。

创建连接实例遵循与事件处理程序订阅时相同的规则:

 struct EventHolder { TEvent<const std::string&> onEvent; }; struct MethodsHolder { void method1( const std::string& ) {} void method2( std::string ) {} void method3( std::string&& ) {} void method4( std::string& ) {} void method5( const int& ) {} }; int main( int argc, char* argv[] ) { EventHolder _eventHolder; MethodsHolder _methodsHolder; EventJoin join1 = EventJoin( _eventHolder.onEvent, METHOD_HANDLER( _methodsHolder, MethodsHolder::method1 ) ); // ok EventJoin join2 = EventJoin( _eventHolder.onEvent, METHOD_HANDLER( _methodsHolder, MethodsHolder::method2 ) ); // ok EventJoin join3 = EventJoin( _eventHolder.onEvent, METHOD_HANDLER( _methodsHolder, MethodsHolder::method3 ) ); // error EventJoin join4 = EventJoin( _eventHolder.onEvent, METHOD_HANDLER( _methodsHolder, MethodsHolder::method4 ) ); // error EventJoin join5 = EventJoin( _eventHolder.onEvent, METHOD_HANDLER( _methodsHolder, MethodsHolder::method5 ) ); // error return 0; } 

另外,您可以“启用” /“禁用”事件处理(原则上已为其创建连接):

 struct EventHolder { TEvent<const std::string&, unsigned int> onEvent; }; struct MethodsHolder { void handleEvent( const std::string& text, unsigned int count ) { std::cout << "Text '" << text << "' handled " << count << " times." << std::endl; } }; int main__( int argc, char* argv[] ) { EventHolder _eventHolder; MethodsHolder methodsHolder; EventJoin eventJoin = EventJoin( _eventHolder.onEvent, METHOD_HANDLER( methodsHolder, MethodsHolder::handleEvent ) ); static const std::string handlingText = "testing..."; for( int i = 0; i < 10; ++i ) { if( eventJoin.isJoined() ) eventJoin.unjoin(); else eventJoin.join(); _eventHolder.onEvent( handlingText, i ); } return 0; } 

 Text 'testing...' handled 0 times. Text 'testing...' handled 2 times. Text 'testing...' handled 4 times. Text 'testing...' handled 6 times. Text 'testing...' handled 8 times. 

总结


首先,值得注意的是,撰写文章的任务越短越好,简洁地完全失败了。

我希望事件处理的结果实现功能强大,并且至少对某些人有用。

一个非常繁琐的示例,演示了主要功能
 #include <iostream> #include <functional> #include "events/event.hpp" #include "events/handler/methodeventhandler.hpp" #include "events/handler/functoreventhandler.hpp" #include "events/join/handlereventjoin.hpp" #include "events/join/eventjoinwrapper.hpp" class Foo { public: Foo() : onMake( m_onMake ), m_onMake(), m_onMakeInner(), m_makeCount( 0 ) { m_onMakeInner += FUNCTOR_HANDLER( m_onMake ); } IEvent<unsigned int>& onMake; void make() { m_onMakeInner( m_makeCount++ ); } private: TEvent<unsigned int> m_onMake, m_onMakeInner; unsigned int m_makeCount; }; namespace instances { Foo& getFoo() { static Foo _foo; return _foo; } } // instances struct FunctorHandler { void operator()( unsigned int makeCount ); }; void functionHandler( unsigned int makeCount ); class ClassHandler { public: void handle( unsigned int makeCount ); }; namespace instances { FunctorHandler& getFunctorHandler() { static FunctorHandler _functorHandler; return _functorHandler; } std::function<void( unsigned int )>& getStdFunctionHandler() { static std::function<void( unsigned int )> _stdFunctionHandler = []( unsigned int makeCount ) { std::cout << "It's std::function handler" << std::endl; if( makeCount >= 2 ) instances::getFoo().onMake -= STD_FUNCTION_HANDLER( instances::getStdFunctionHandler() ); }; return _stdFunctionHandler; } ClassHandler& getClassHandler() { static ClassHandler _classHandler; return _classHandler; } } // instances void FunctorHandler::operator()( unsigned int makeCount ) { std::cout << "It's functor handler" << std::endl; if( makeCount >= 0 ) instances::getFoo().onMake -= FUNCTOR_HANDLER( instances::getFunctorHandler() ); } void functionHandler( unsigned int makeCount ) { std::cout << "It's function handler" << std::endl; if( makeCount >= 3 ) instances::getFoo().onMake -= FUNCTION_HANDLER( functionHandler ); } void ClassHandler::handle( unsigned int makeCount ) { std::cout << "It's method handler" << std::endl; if( makeCount >= 4 ) instances::getFoo().onMake -= MY_METHOD_HANDLER( ClassHandler::handle ); } int main( int argc, char* argv[] ) { Foo& foo = instances::getFoo(); auto lambdaHandler = []( unsigned int ) { std::cout << "It's lambda handler" << std::endl; }; foo.onMake += FUNCTOR_HANDLER( instances::getFunctorHandler() ); foo.onMake += LAMBDA_HANDLER( lambdaHandler ); EventJoin lambdaJoin = foo.onMake += LAMBDA_HANDLER( ( [ &foo, &lambdaHandler ]( unsigned int makeCount ) { if( makeCount >= 1 ) foo.onMake -= LAMBDA_HANDLER( lambdaHandler ); } ) ); foo.onMake += STD_FUNCTION_HANDLER( instances::getStdFunctionHandler() ); foo.onMake += FUNCTION_HANDLER( functionHandler ); foo.onMake += METHOD_HANDLER( instances::getClassHandler(), ClassHandler::handle ); for( int i = 0; i < 6; ++i ) { std::cout << "Make " << i << " time:" << std::endl; foo.make(); std::cout << std::endl; } lambdaJoin.unjoin(); return 0; } 

结论:

 Make 0 time: It's functor handler It's lambda handler It's std::function handler It's function handler It's method handler Make 1 time: It's lambda handler It's std::function handler It's function handler It's method handler Make 2 time: It's std::function handler It's function handler It's method handler Make 3 time: It's function handler It's method handler Make 4 time: It's method handler Make 5 time: 


值得注意的一些重要点:

  • 它没有单独指定,因此我将提及该实现中的事件本身是函子,这意味着它可以充当另一个事件的处理程序;
  • 现在您不能使用常量方法(类成员函数)作为处理程序;我认为,如果需要这样的机会,那么在现有的基础上为此编写一种新类型的处理程序并不难。

此外,在最终版本中,文章中省略了一些要点,以提高代码的可见性和可读性:

  • 相应处理程序的方法返回值的类型(类的成员函数)可以是任意值,不一定是空的(对于处理程序函数,也可以这样做);
  • 整个实现都包装在名称空间中,以便在项目中易于使用(如果这对某人似乎是多余的,则可以随时将其删除);
  • 一些地方添加了noexcept说明符

对于所有至少在对角线阅读过的人来说,低弓。我附上所有代码;也可以在此处使用(具有所有最新改进)。

整个代码
./events/helpers/is_equatable.hpp
 #pragma once #include <type_traits> template<class T> class is_equatable { private: template<class U> static constexpr std::true_type exists( decltype( std::declval<U>() == std::declval<U>() )* = nullptr ) noexcept; template<class U> static constexpr std::false_type exists( ... ) noexcept; public: static constexpr bool value = decltype( exists<T>( nullptr ) )::value; }; 


./events/handlers/abstracteventhandler.hpp
 #pragma once #include "eventhandlerptr.h" namespace events { namespace handlers { template<class ...TParams> class AbstractEventHandler { using MyType = AbstractEventHandler<TParams...>; public: virtual ~AbstractEventHandler() {} virtual void call( TParams... params ) = 0; bool operator==( const MyType& other ) const noexcept { return isEquals( other ); } bool operator!=( const MyType& other ) const noexcept { return !( *this == other ); } protected: AbstractEventHandler() {} virtual bool isEquals( const MyType& other ) const noexcept = 0; }; } // handlers } // events 


./events/handlers/eventhandlerptr.h
 #pragma once #include <memory> namespace events { namespace handlers { template<class ...TParams> class AbstractEventHandler; template<class ...Types> using TEventHandlerPtr = std::shared_ptr<AbstractEventHandler<Types...>>; } // handlers } // events 


./events/handlers/functoreventhandler.hpp
 #pragma once #include <memory> #include <assert.h> #include "abstracteventhandler.hpp" #include "../helpers/is_equatable.hpp" namespace events { namespace handlers { namespace { template<class TFunctor, class ...TParams> struct IsFunctorParamsCompatible { private: template<class TCheckedFunctor, class ...TCheckedParams> static constexpr std::true_type exists( decltype( std::declval<TCheckedFunctor>()( std::declval<TCheckedParams>()... ) )* = nullptr ) noexcept; template<class TCheckedFunctor, class ...TCheckedParams> static constexpr std::false_type exists( ... ) noexcept; public: static constexpr bool value = decltype( exists<TFunctor, TParams...>( nullptr ) )::value; }; } // template<class TFunctor> class FunctorHolder; template<class TFunctor, class ...TParams> class FunctorEventHandler : public AbstractEventHandler<TParams...> { using MyType = FunctorEventHandler<TFunctor, TParams...>; using TFunctorHolderPtr = std::shared_ptr<FunctorHolder<TFunctor>>; public: FunctorEventHandler( TFunctorHolderPtr functorHolder ) : AbstractEventHandler<TParams...>(), m_functorHolder( functorHolder ) { assert( m_functorHolder != nullptr ); } virtual void call( TParams... params ) override { static_assert( IsFunctorParamsCompatible<TFunctor, TParams...>::value, "Event and functor arguments are not compatible" ); m_functorHolder->m_functor( params... ); } protected: virtual bool isEquals( const AbstractEventHandler<TParams...>& other ) const noexcept override { const MyType* _other = dynamic_cast<const MyType*>( &other ); return ( _other != nullptr && *m_functorHolder == *_other->m_functorHolder ); } private: TFunctorHolderPtr m_functorHolder; }; namespace { template<class TEqu, class TEnabled = void> struct EqualityChecker; template<class TEquatable> struct EqualityChecker<TEquatable, typename std::enable_if<is_equatable<TEquatable>::value>::type> { static constexpr bool isEquals( const TEquatable& operand1, const TEquatable& operand2 ) noexcept { return ( operand1 == operand2 ); } }; template<class TNonEquatable> struct EqualityChecker<TNonEquatable, typename std::enable_if<!is_equatable<TNonEquatable>::value>::type> { static constexpr bool isEquals( const TNonEquatable& operand1, const TNonEquatable& operand2 ) noexcept { return ( &operand1 == &operand2 ); } }; } // template<class TFunctor> class FunctorHolder { using MyType = FunctorHolder<TFunctor>; public: template<class ...TCallParams> operator TEventHandlerPtr<TCallParams...>() { return TEventHandlerPtr<TCallParams...>( new FunctorEventHandler<TFunctor, TCallParams...>( m_me.lock() ) ); } bool operator==( const MyType& other ) const noexcept { return EqualityChecker<TFunctor>::isEquals( m_functor, other.m_functor ); } bool operator!=( const MyType& other ) const noexcept { return !( *this == other ); } template<class TFunctor> static std::shared_ptr<MyType> create( TFunctor&& functor ) { std::shared_ptr<MyType> result( new MyType( functor ) ); result->m_me = result; return result; } private: FunctorHolder( TFunctor& functor ) : m_functor( functor ), m_me() { } TFunctor& m_functor; std::weak_ptr<MyType> m_me; template<class TFunctor, class ...TParams> friend class FunctorEventHandler; }; template<class TFunctor> std::shared_ptr<FunctorHolder<TFunctor>> createFunctorEventHandler( TFunctor&& functor ) { return FunctorHolder<TFunctor>::create( functor ); } } // handlers } // events #define FUNCTOR_HANDLER( Functor ) ::events::handlers::createFunctorEventHandler( Functor ) #define LAMBDA_HANDLER( Lambda ) FUNCTOR_HANDLER( Lambda ) #define STD_FUNCTION_HANDLER( StdFunction ) FUNCTOR_HANDLER( StdFunction ) #define FUNCTION_HANDLER( Function ) FUNCTOR_HANDLER( &Function ) 


./events/handlers/methodeventhandler.hpp
 #pragma once #include <memory> #include <assert.h> #include "abstracteventhandler.hpp" namespace events { namespace handlers { namespace { template<class TMethodHolder, class ...TParams> struct IsMethodParamsCompatible { private: template<class TCheckedMethodHolder, class ...TCheckedParams> static constexpr std::true_type exists( decltype( ( std::declval<TCheckedMethodHolder>().m_object.*std::declval<TCheckedMethodHolder>().m_method )( std::declval<TCheckedParams>()... ) )* = nullptr ) noexcept; template<class TCheckedMethodHolder, class ...TCheckedParams> static constexpr std::false_type exists( ... ) noexcept; public: static constexpr bool value = decltype( exists<TMethodHolder, TParams...>( nullptr ) )::value; }; } // template<class TMethodHolder, class ...TParams> class MethodEventHandler : public AbstractEventHandler<TParams...> { using MyType = MethodEventHandler<TMethodHolder, TParams...>; using TMethodHolderPtr = std::shared_ptr<TMethodHolder>; public: MethodEventHandler( TMethodHolderPtr methodHolder ) : AbstractEventHandler<TParams...>(), m_methodHolder( methodHolder ) { assert( m_methodHolder != nullptr ); } virtual void call( TParams... params ) override { static_assert( IsMethodParamsCompatible<TMethodHolder, TParams...>::value, "Event and method arguments are not compatible" ); ( m_methodHolder->m_object.*m_methodHolder->m_method )( params... ); } protected: virtual bool isEquals( const AbstractEventHandler<TParams...>& other ) const noexcept override { const MyType* _other = dynamic_cast<const MyType*>( &other ); return ( _other != nullptr && *m_methodHolder == *_other->m_methodHolder ); } private: TMethodHolderPtr m_methodHolder; }; template<class TObject, class TResult, class ...TParams> class MethodHolder { using MyType = MethodHolder<TObject, TResult, TParams...>; using TMethod = TResult( TObject::* )( TParams... ); public: template<class ...TCallParams> operator TEventHandlerPtr<TCallParams...>() { return TEventHandlerPtr<TCallParams...>( new MethodEventHandler<MyType, TCallParams...>( m_me.lock() ) ); } bool operator==( const MyType& other ) const noexcept { return ( &m_object == &other.m_object && m_method == other.m_method ); } bool operator!=( const MyType& other ) const noexcept { return !( *this == other ); } template<class TObject, class ...TParams> static std::shared_ptr<MyType> create( TObject& object, TMethod method ) { std::shared_ptr<MyType> result( new MyType( object, method ) ); result->m_me = result; return result; } private: MethodHolder( TObject& object, TMethod method ) : m_object( object ), m_method( method ) { assert( m_method != nullptr ); } TObject& m_object; TMethod m_method; std::weak_ptr<MyType> m_me; template<class TMethodHolder, class ...TParams> friend class MethodEventHandler; template<class TMethodHolder, class ...TParams> friend struct IsMethodParamsCompatible; }; template<class TObject, class TResult, class ...TParams> std::shared_ptr<MethodHolder<TObject, TResult, TParams...>> createMethodEventHandler( TObject& object, TResult( TObject::*method )( TParams... ) ) { return MethodHolder<TObject, TResult, TParams...>::create( object, method ); } } // handlers } // events #define METHOD_HANDLER( Object, Method ) ::events::handlers::createMethodEventHandler( Object, &Method ) #define MY_METHOD_HANDLER( Method ) METHOD_HANDLER( *this, Method ) 


./events/handlers/handlercast.hpp
 #pragma once #include <memory> #include "eventhandlerptr.h" namespace events { namespace handlers { template<class TSome> struct HandlerCast { template<class ...Types> static constexpr TEventHandlerPtr<Types...> cast( TSome& some ) { return static_cast<TEventHandlerPtr<Types...>>( some ); } }; template<class TPtr> struct HandlerCast<std::shared_ptr<TPtr>> { template<class ...Types> static constexpr TEventHandlerPtr<Types...> cast( std::shared_ptr<TPtr> some ) { return HandlerCast<TPtr>::cast<Types...>( *some ); } }; } // handlers } // events 


./events/event.hpp
 #pragma once #include <type_traits> #include <list> #include <memory> #include <shared_mutex> #include <algorithm> #include <assert.h> #include "handlers/abstracteventhandler.hpp" #include "handlers/eventhandlerptr.h" #include "handlers/handlercast.hpp" #include "joins/eventjoinwrapper.hpp" namespace events { namespace joins { template<class ...TParams> class HandlerEventJoin; } template<class ...TParams> class IEvent { public: template<class TSome> EventJoin operator+=( TSome&& some ) { EventJoin result( *this, std::forward<TSome>( some ) ); result.join(); return result; } template<class TSome> bool operator-=( TSome&& some ) { return removeHandler( handlers::HandlerCast<TSome>::cast<TParams...>( some ) ); } protected: using TMyEventHandlerPtr = handlers::TEventHandlerPtr<TParams...>; IEvent() {} virtual bool isHandlerAdded( const TMyEventHandlerPtr& eventHandler ) const = 0; virtual bool addHandler( TMyEventHandlerPtr eventHandler ) = 0; virtual bool removeHandler( TMyEventHandlerPtr eventHandler ) = 0; friend class joins::HandlerEventJoin<TParams...>; }; template<class ...TParams> class TEvent : public IEvent<TParams...> { using TEventHandlerIt = typename std::list<TMyEventHandlerPtr>::const_iterator; public: TEvent() : m_handlers(), m_currentIt(), m_isCurrentItRemoved( false ), m_handlerListMutex() { } 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(); } protected: virtual bool isHandlerAdded( const TMyEventHandlerPtr& eventHandler ) const override { std::shared_lock<std::shared_mutex> _handlerListMutexLock( m_handlerListMutex ); return ( findEventHandler( eventHandler ) != m_handlers.end() ); } virtual bool addHandler( TMyEventHandlerPtr eventHandler ) override { std::unique_lock<std::shared_mutex> _handlerListMutexLock( m_handlerListMutex ); if( findEventHandler( eventHandler ) == m_handlers.end() ) { m_handlers.push_back( std::move( eventHandler ) ); return true; } return false; } virtual bool removeHandler( TMyEventHandlerPtr eventHandler ) override { std::unique_lock<std::shared_mutex> _handlerListMutexLock( m_handlerListMutex ); auto it = findEventHandler( eventHandler ); if( it != m_handlers.end() ) { if( it == m_currentIt ) m_isCurrentItRemoved = true; else deleteHandler( it ); return true; } return false; } private: //      'm_handlerListMutex' inline TEventHandlerIt findEventHandler( const TMyEventHandlerPtr& eventHandler ) const noexcept { return std::find_if( m_handlers.cbegin(), m_handlers.cend(), [ &eventHandler ]( const TMyEventHandlerPtr& oneHandler ) { return ( *oneHandler == *eventHandler ); } ); } //      'm_handlerListMutex' inline void deleteHandler( TEventHandlerIt it ) { m_handlers.erase( it ); } std::list<TMyEventHandlerPtr> m_handlers; //    'm_handlerListMutex' mutable TEventHandlerIt m_currentIt; mutable bool m_isCurrentItRemoved; mutable std::shared_mutex m_handlerListMutex; }; } // events 


./events/joins/abstracteventjoin.h
 #pragma once namespace events { namespace joins { class AbstractEventJoin { public: virtual ~AbstractEventJoin(); virtual bool isJoined() const = 0; virtual bool join() = 0; virtual bool unjoin() = 0; protected: AbstractEventJoin(); }; } // joins } // events 


./events/joins/abstracteventjoin.cpp
 #include "abstracteventjoin.h" namespace events { namespace joins { AbstractEventJoin::AbstractEventJoin() { } AbstractEventJoin::~AbstractEventJoin() { } } // joins } // events 


./events/joins/handlereventjoin.h
 #pragma once #include "abstracteventjoin.h" #include "../handlers/eventhandlerptr.h" namespace events { template<class ...TParams> class IEvent; namespace joins { template<class ...TParams> class HandlerEventJoin : public AbstractEventJoin { public: HandlerEventJoin( IEvent<TParams...>& _event, ::events::handlers::TEventHandlerPtr<TParams...> handler ) : AbstractEventJoin(), m_event( _event ), m_handler( handler ) { } virtual inline bool isJoined() const override; virtual inline bool join() override; virtual inline bool unjoin() override; private: IEvent<TParams...>& m_event; ::events::handlers::TEventHandlerPtr<TParams...> m_handler; }; } // joins } // events 


./events/joins/handlereventjoin.hpp
 #pragma once #include "handlereventjoin.h" #include "../event.hpp" namespace events { namespace joins { template<class ...TParams> bool HandlerEventJoin<TParams...>::isJoined() const { return m_event.isHandlerAdded( m_handler ); } template<class ...TParams> bool HandlerEventJoin<TParams...>::join() { return m_event.addHandler( m_handler ); } template<class ...TParams> bool HandlerEventJoin<TParams...>::unjoin() { return m_event.removeHandler( m_handler ); } } // joins } // events 


./events/joins/eventjoinwrapper.h
 #pragma once #include <memory> #include "../handlers/eventhandlerptr.h" namespace events { template<class ...TParams> class IEvent; namespace joins { class AbstractEventJoin; class EventJoinWrapper { public: template<class TSome, class ...TParams> inline EventJoinWrapper( IEvent<TParams...>& _event, TSome&& handler ); constexpr EventJoinWrapper() noexcept; EventJoinWrapper( EventJoinWrapper&& other ) noexcept; EventJoinWrapper( EventJoinWrapper& other ) noexcept; EventJoinWrapper& operator=( EventJoinWrapper&& other ) noexcept; EventJoinWrapper& operator=( const EventJoinWrapper& other ) noexcept; operator bool() const; bool isAssigned() const; bool isJoined() const; bool join(); bool unjoin(); private: std::shared_ptr<AbstractEventJoin> m_eventJoin; }; } // joins using EventJoin = joins::EventJoinWrapper; } // events 


./events/joins/eventjoinwrapper.hpp
 #pragma once #include "eventjoinwrapper.h" #include "handlereventjoin.h" #include "../handlers/handlercast.hpp" namespace events { namespace joins { template<class TSome, class ...TParams> EventJoinWrapper::EventJoinWrapper( IEvent<TParams...>& _event, TSome&& handler ) : m_eventJoin( std::make_shared<HandlerEventJoin<TParams...>>( _event, ::events::handlers::HandlerCast<TSome>::cast<TParams...>( handler ) ) ) { } } // joins } // events 


./events/joins/eventjoinwrapper.cpp
 #include "eventjoinwrapper.h" #include <type_traits> #include "abstracteventjoin.h" namespace events { namespace joins { constexpr EventJoinWrapper::EventJoinWrapper() noexcept : m_eventJoin( nullptr ) { } EventJoinWrapper::EventJoinWrapper( EventJoinWrapper&& other ) noexcept : m_eventJoin( std::move( other.m_eventJoin ) ) { } EventJoinWrapper::EventJoinWrapper( EventJoinWrapper& other ) noexcept : m_eventJoin( other.m_eventJoin ) { } EventJoinWrapper& EventJoinWrapper::operator=( EventJoinWrapper&& other ) noexcept { m_eventJoin = std::move( other.m_eventJoin ); return *this; } EventJoinWrapper& EventJoinWrapper::operator=( const EventJoinWrapper& other ) noexcept { m_eventJoin = other.m_eventJoin; return *this; } EventJoinWrapper::operator bool() const { return isJoined(); } bool EventJoinWrapper::isAssigned() const { return ( m_eventJoin != nullptr ); } bool EventJoinWrapper::isJoined() const { return ( m_eventJoin != nullptr && m_eventJoin->isJoined() ); } bool EventJoinWrapper::join() { return ( m_eventJoin != nullptr ? m_eventJoin->join() : false ); } bool EventJoinWrapper::unjoin() { return ( m_eventJoin != nullptr ? m_eventJoin->unjoin() : false ); } } // joins } // events 



UPD1。在此处和本文的前面,给出了用VC ++ 14编写的代码为了与其他编译器兼容,最好从链接中获取代码。特别感谢Cheater提供与GCC的兼容性
UPD2。谢谢lexxmark,它看到的螺纹孔中的多个同时发生的事件调用条款。
小改进
 namespace { template<class ...TParams> struct TypeHelper { using TEventHandlerPtr = handlers::TEventHandlerPtr<TParams...>; using TEventHandlerIt = typename std::list<TEventHandlerPtr>::const_iterator; }; } // template<class ...TParams> class IEvent { . . . protected: using TMyEventHandlerPtr = typename TypeHelper<TParams...>::TEventHandlerPtr; . . . }; namespace { template<class ...TParams> struct EventCore { using TMyHandlerPtr = typename TypeHelper<TParams...>::TEventHandlerPtr; std::list<TMyHandlerPtr> handlers; mutable std::shared_mutex coreMutex; }; template<class ...TParams> class HandlerRunner { using TMyEventCore = EventCore<TParams...>; using TMyHandlerIt = typename TypeHelper<TParams...>::TEventHandlerIt; public: HandlerRunner( TMyEventCore& eventCore ) : m_eventCore( eventCore ), currentIt(), wasRemoving( false ) { } void run( TParams... params ) { m_eventCore.coreMutex.lock_shared(); currentIt = m_eventCore.handlers.begin(); wasRemoving = false; while( currentIt != m_eventCore.handlers.end() ) { m_eventCore.coreMutex.unlock_shared(); ( *currentIt )->call( params... ); m_eventCore.coreMutex.lock_shared(); if( wasRemoving ) wasRemoving = false; else ++currentIt; } m_eventCore.coreMutex.unlock_shared(); } TMyHandlerIt currentIt; mutable bool wasRemoving; private: TMyEventCore& m_eventCore; }; } // template<class ...TParams> class TEvent : public IEvent<TParams...> { using TMyEventHandlerPtr = typename TypeHelper<TParams...>::TEventHandlerPtr; using TMyEventHandlerIt = typename TypeHelper<TParams...>::TEventHandlerIt; using TMyHandlerRunner = HandlerRunner<TParams...>; public: TEvent() : m_core() { } 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_core.coreMutex.lock_shared(); m_handlerRunners.erase( it ); m_core.coreMutex.unlock_shared(); } protected: virtual bool isHandlerAdded( const TMyEventHandlerPtr& eventHandler ) const override { std::shared_lock<std::shared_mutex> _coreMutexLock( m_core.coreMutex ); return ( findEventHandler( eventHandler ) != m_core.handlers.end() ); } virtual bool addHandler( TMyEventHandlerPtr eventHandler ) override { std::unique_lock<std::shared_mutex> _coreMutexLock( m_core.coreMutex ); if( findEventHandler( eventHandler ) == m_core.handlers.end() ) { m_core.handlers.push_back( std::move( eventHandler ) ); return true; } return false; } virtual bool removeHandler( TMyEventHandlerPtr eventHandler ) override { std::unique_lock<std::shared_mutex> _coreMutexLock( m_core.coreMutex ); auto it = findEventHandler( eventHandler ); if( it != m_core.handlers.end() ) { for( TMyHandlerRunner* oneHandlerRunner : m_handlerRunners ) { if( it == oneHandlerRunner->currentIt ) { ++oneHandlerRunner->currentIt; oneHandlerRunner->wasRemoving = true; } } m_core.handlers.erase( it ); return true; } return false; } private: //      'm_core.coreMutex' inline TMyEventHandlerIt findEventHandler( const TMyEventHandlerPtr& eventHandler ) const { return std::find_if( m_core.handlers.cbegin(), m_core.handlers.cend(), [ &eventHandler ]( const TMyEventHandlerPtr& oneHandler ) { return ( *oneHandler == *eventHandler ); } ); } EventCore<TParams...> m_core; std::list<TMyHandlerRunner*> m_handlerRunners; }; 

(, , ) HandlerRunner , . , : currentIt ( ) wasRemoving (, ). HandlerRunner' operator() ; (, ) , EventCore . T.O. , , , , , .
UPD3。多亏isullxbh,发现另一个错误。它与不正确的存储以及随后对rvalue传递的对象(主要是lambda表达式)的访问有关。
改正
, lvalue , lvalue -, , rvalue , (, ). :
 template<class TSome> struct ObjectSaver; template<class LValue> struct ObjectSaver<LValue&> { using TObject = LValue&; }; template<class RValue> struct ObjectSaver<RValue&&> { using TObject = RValue; }; 

Holder ( lvalue rvalue ), , «» . type erasing ( ). , Holder' .
 template<class TBase> struct AbstractInnerHolder { virtual ~AbstractInnerHolder() {} virtual inline TBase& get() = 0; inline const TBase& get() const { return const_cast<AbstractInnerHolder<TBase>&>( *this ).get(); } }; template<class TBase, class TInner> struct TInnerHolder : public AbstractInnerHolder<TBase> { using TInnerObject = typename ObjectSaver<TInner>::TObject; TInnerHolder( TInner _inner ) : AbstractInnerHolder<TBase>(), inner( std::forward<TInner>( _inner ) ) { } virtual inline TBase& get() override { return static_cast<TBase&>( inner ); } TInnerObject inner; }; template<class TAssignBase, class TArgInner> AbstractInnerHolder<TAssignBase>& createInnerHolder( TArgInner&& inner ) { using TAssignInner = decltype( inner ); return *new TInnerHolder<TAssignBase, TAssignInner>( std::forward<TArgInner>( inner ) ); } 

Holder' . MethodHolder' .
 template<class TObject, class TResult, class ...TParams> class MethodHolder { . . . using MyType = MethodHolder<TObject, TResult, TParams...>; using TMethod = TResult( TObject::* )( TParams... ); public: ~MethodHolder() { delete &m_innerHolder; } bool operator==( const MyType& other ) const { return ( &m_innerHolder.get() == &other.m_innerHolder.get() && m_method == other.m_method ); } template<class TArgObject> static std::shared_ptr<MyType> create( TArgObject&& object, TMethod method ) { std::shared_ptr<MyType> result( new MyType( std::forward<TArgObject>( object ), method ) ); result->m_me = result; return result; } private: template<class TArgObject> MethodHolder( TArgObject&& object, TMethod method ) : m_innerHolder( createInnerHolder<TObject>( std::forward<TArgObject>( object ) ) ), m_method( method ) { assert( m_method != nullptr ); } AbstractInnerHolder<TObject>& m_innerHolder; TMethod m_method; std::weak_ptr<MyType> m_me; . . . }; 

 namespace { template<class TMethodHolder, class ...TParams> struct IsMethodParamsCompatible { private: template<class TCheckedMethodHolder, class ...TCheckedParams> static constexpr std::true_type exists( decltype( ( std::declval<TCheckedMethodHolder>().m_innerHolder.get().*std::declval<TCheckedMethodHolder>().m_method )( std::declval<TCheckedParams>()... ) )* = nullptr ); . . . }; } // template<class TMethodHolder, class ...TParams> class MethodEventHandler : public AbstractEventHandler<TParams...> { public: virtual void call( TParams... params ) override { static_assert( IsMethodParamsCompatible<TMethodHolder, TParams...>::value, "Event and method arguments are not compatible" ); ( m_methodHolder->m_innerHolder.get().*m_methodHolder->m_method )( params... ); } private: TMethodHolderPtr m_methodHolder; . . . }; 

 template<class TObject, class TResult, class ...TParams> std::shared_ptr<MethodHolder<typename std::decay<TObject>::type, TResult, TParams...>> createMethodEventHandler( TObject&& object, TResult( std::decay<TObject>::type::*method )( TParams... ) ) { return MethodHolder<std::decay<TObject>::type, TResult, TParams...>::create( std::forward<TObject>( object ), method ); } 

做完了 FunctorHolder . . - .

PS与Qt信号/时隙机制的比较


如果我说Qt是用C ++开发的非常普遍的框架,我想我不会弄错除其他外,它还具有自己的事件处理机制,其中有作为事件类似物的信号和作为处理程序类似物的插槽。它是使用Meta-Object编译器实现的,后者是更全局的Meta-Object System的一部分,而后者又是使用Qt over C ++中使用的附件实现的

两种实现的功能:


  • 将信号(事件)连接到方法(成员函数),函子和函数的能力;
  • () (), ( lvalue , rvalue );
  • ( );
  • () ( ).

Qt :



Qt :


  • QObject ;
    , , QObject , , , ( : Virtual inheritance with QObject is not supported. ); , , ;
  • template';
    , public - QObject ; moc' ; ,
    ,
     #include <QObject> class AbstractProperty : public QObject { Q_OBJECT protected: AbstractProperty(); signals: void valueChanged(); }; template<class TSource> class TProperty : public AbstractProperty { public: TProperty( const TSource& value = TSource() ) : AbstractProperty(), m_value( value ) { } const TSource& value() const { return m_value; } void setValue( const TSource& newValue ) { if( newValue != m_value ) { m_value = newValue; emit valueChanged(); } } private: TSource m_value; }; 

    , valueChanged ( , ) , .
    , , ;
  • .cpp-;
    ;
  • QMetaObject::Connection ;
    , Qt ( ) , ; () , , ; , ; Qt ;
  • 使用moc额外生成的代码;
    这已经完全是主观的,但是决定是,对于每个使用信号和插槽的类(并不总是使用插槽),会生成多个文件(每种配置按文件排列),这会带来一些不便;但老实说,这是最轻微的缺陷。

重要的是要注意,与Qt的比较仍然是非常主观的,并不旨在高举或谴责该框架。必须记住,除了信号/时隙机制外,Qt还提供了强大的功能,既使用此机制,又不依赖于此。无论如何,始终由您决定使用什么以及不使用什么。

Source: https://habr.com/ru/post/zh-CN424593/


All Articles