Reputation: 8315
I'm trying to write a template event class and keep as much code as possible inside this base class. I'm using the curiously recurring template pattern, but i'm not exactly sure about what i'm doing here.
template< class EventType >
class Event
{
protected:
std::vector< EventType::Listener * > m_aListeners;
public:
void operator += ( EventType::Listener * pListener )
{
m_aListeners.push_back( pListener );
}
void operator -= ( EventType::Listener * pListener )
{
std::vector< EventType::Listener * >::reverse_iterator revIter = m_aListeners.rbegin();
for( ; revIter != m_aListeners.rend(); ++revIter )
if( revIter == pListener )
{
m_aListeners.remove( revIter );
break;
}
}
void Trigger( EventType::Data * pData )
{
std::vector< EventType::Listener * >::iterator iter = m_aListeners.begin();
for( ; iter != m_aListeners.end(); ++iter )
CallListenert( iter, pData );
}
virtual void CallListener( EventType::Listener * pListener, EventType::Data * pData ) = 0;
};
And its subclass for a given event type :
class ConnectionSuccessEvent : public Event< ConnectionSuccessEvent >
{
public:
class Data
{
public:
Data( int iVal ) : m_iVal( iVal ) { }
public:
const int m_iVal;
};
class Listener
{
public:
virtual ~Listener() { }
virtual void OnConnectionSuccess( Data * pEventData ) = 0;
};
void CallListener( Listener * pListener, Data * pData )
{
pListener->OnConnectionSuccess( pData );
}
};
And a class implementing the listener class :
class MyClass : public ConnectionSuccessEvent::Listener
{
public:
void OnConnectionSuccess( ConnectionSuccessEvent::Data * pEventData )
{
std::cout << "OnConnectionSuccess : " << pEventData->m_iVal << std::endl;
}
};
Which i use as follows :
MyClass oMyClassInstance;
ConnectionSuccessEvent oOnConnectionSuccess;
oOnConnectionSuccess += & oMyClassInstance;
oOnConnectionSuccess += & oMyClassInstance;
ConnectionSuccessEvent::Data oData( 456 );
oOnConnectionSuccess.Trigger( & oData );
oOnConnectionSuccess -= & oMyClassInstance;
oOnConnectionSuccess -= & oMyClassInstance;
This is resulting in several compilations errors, the first one being :
Error 2 error C2059: syntax error : '>' c:\dev\eventtest\event.h 16
Which corresponds to the declaration of the vector m_aListeners.
I have two questions :
What is causing my error ? Am I not allowed to use EventType::Listener in my class Event ?
How is the curiously recurring pattern possible ? I mean, to be defined, the derived class requires its parent class be defined. But since it's parent it's parent class requires it's template parameter class to be defined i.e it's base class), then it's a problem, because for one to be defined, it needs the other one to be defined first. This is like having an instance of A in class B, and an instance ob B in class A.
Thank you !
EDIT : I can't use C++11.
Upvotes: 0
Views: 136
Reputation: 8315
I found an acceptably ugly way to do that !
Base event class containing the behaviour of the event objects.
template< class EventListenerType, class EventDataType >
class BaseEvent
{
private:
std::vector< EventListenerType * > m_aListeners;
public:
void operator += ( EventListenerType * pListener )
{
m_aListeners.push_back( pListener );
}
void operator -= ( EventListenerType * pListener )
{
// When nI is 0 and gets decremented (i.e would be < 0), it "underflows" and
// becomes >= m_aListeners.size(), so we can detect that the last element (i.e
// the first in position ) has been treated when nI >= m_aListeners.size().
for( size_t nI = m_aListeners.size() - 1; nI <= m_aListeners.size(); --nI )
{
if( m_aListeners[ nI ] == pListener )
{
m_aListeners.erase( m_aListeners.begin() + nI );
break;
}
}
}
void Trigger( EventDataType * pData )
{
for( size_t nI = 0; nI < m_aListeners.size(); ++nI )
m_aListeners[ nI ]->OnEvent( pData );
}
};
And :
template< class EventDataType >
class EventListener
{
public:
virtual ~EventListener() { }
virtual void OnEvent( const EventDataType * pData ) = 0;
};
ConnectionSuccessEvent specific stuff :
class ConnectionSuccessEventData
{
public:
const int m_iVal;
ConnectionSuccessEventData( int iVal ) : m_iVal( iVal ) { }
};
typedef EventListener< ConnectionSuccessEventData > ConnectionSuccessEventListener;
typedef BaseEvent< ConnectionSuccessEventListener, ConnectionSuccessEventData > ConnectionSuccessEvent;
DataReceivedEvent specific stuff :
class DataReceivedEventData
{
public:
const float m_fVal;
DataReceivedEventData( float fVal ) : m_fVal( fVal ) { }
};
typedef EventListener< DataReceivedEventData > DataReceivedEventListener;
typedef BaseEvent< DataReceivedEventListener, DataReceivedEventData > DataReceivedEvent;
Listener class :
class MyClass : public ConnectionSuccessEventListener, public DataReceivedEventListener
{
public:
void OnEvent( const ConnectionSuccessEventData * pData )
{
std::cout << "Connection success event : " << pData->m_iVal << std::endl;
}
void OnEvent( const DataReceivedEventData * pData )
{
std::cout << "Data received event : " << pData->m_fVal << std::endl;
}
};
Usage :
MyClass oMyClassInstance;
ConnectionSuccessEvent oOnConnectionSuccess;
DataReceivedEvent oOnDataReceived;
oOnConnectionSuccess += & oMyClassInstance;
oOnDataReceived += & oMyClassInstance;
oOnConnectionSuccess += & oMyClassInstance;
oOnDataReceived += & oMyClassInstance;
ConnectionSuccessEventData oConnectionSuccessData( 123 );
oOnConnectionSuccess.Trigger( & oConnectionSuccessData );
DataReceivedEventData oDataReceivedData( 0.0f );
oOnDataReceived.Trigger( & oDataReceivedData );
oOnDataReceived += & oMyClassInstance;
oOnConnectionSuccess -= & oMyClassInstance;
oOnDataReceived += & oMyClassInstance;
oOnConnectionSuccess -= & oMyClassInstance;
Upvotes: 1