0

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.

Virus721
  • 8,061
  • 12
  • 67
  • 123
  • `typename EventType::Listener` for every occurence of `EventType::Listener` – Piotr Skotnicki Oct 21 '15 at 11:49
  • Thanks for your help. Is this C++11 specific ? Because i cannot use C++11. – Virus721 Oct 21 '15 at 11:50
  • @Virus721: No. It was there before C++11. – Nawaz Oct 21 '15 at 11:51
  • @PiotrSkotnicki It seems to work better. But now i'm getting another error : `Error 1 error C2039: 'Listener' : is not a member of 'core::ConnectionSuccessEvent' c:\dev\eventtest\event.h 16` at the same location, in the vector definition. Did i forget something ? I checked the spelling. – Virus721 Oct 21 '15 at 11:54
  • 1
    See ["Where and why do I have to put the “template” and “typename” keywords?"](http://stackoverflow.com/questions/610245/where-and-why-do-i-have-to-put-the-template-and-typename-keywords). – Some programmer dude Oct 21 '15 at 11:56
  • @Virus721 it's an incomplete type at this point – Piotr Skotnicki Oct 21 '15 at 11:58
  • So it is not possible to use, in the base class of the CRTP, classes that are nested into the child class ? – Virus721 Oct 21 '15 at 12:06
  • any particular reason that you're not just using the excellent boost::signals library? – Richard Hodges Oct 21 '15 at 12:17
  • @RichardHodges Well mainly because of the platform i'm targetting. I know someone who had a lot of problems when trying to use boost on it. And also because this is on occasion to learn something more about C++. – Virus721 Oct 21 '15 at 12:20
  • @Virus721 understood. suggest you have a look at boost::signals (header-only) source code. There are many subtle problems you will need to overcome in this problem domain (such as recursion, subscriber lifetime management etc) which are neatly and non-trivially solved there. You'll learn a lot. – Richard Hodges Oct 21 '15 at 12:23
  • I found a way to "fix" my problem. Would any of you like to give me his/her opinion ? Thanks for the help anyway. – Virus721 Oct 21 '15 at 13:20

1 Answers1

1

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;
Virus721
  • 8,061
  • 12
  • 67
  • 123