Question

I have little experience with generic programming (I've rarely needed it), but I'm now trying to implement an event system for my application. This is what I had in mind for the actual events:

template <typename Arg_Type> struct Event {
    std::vector<std::string> ids; // All the objects to pass the event to
    Action action;                // Action to perform (enum)
    std::vector<Arg_Type> args;   // List of event-dependant arguments
};

But then I'd like to store my various events in a vector:

std::vector<Event*> events;

Unfortunately it doesn't seem to be that straightforward!

How would I go about achieving the desired effect? That is, being able to pass around events with varying kinds of argument types?

I know I could do something like this:

struct Event {};

struct EventFloat : public Event {};

struct EventString : public Event {};

But it just seems inelegant and harder to maintain.

Was it helpful?

Solution

Make your Event<> classes inherit from a common parent, say EventInterface. The EventInterface would be an abstract class that defines the interface to all events.

class EventInterface
{
    public:
        virtual void tweak  ( int t ) = 0;
        virtual void twiddle( int t ) = 0;
        virtual void frob   ( int f ) = 0;

        virtual ~EventInterface() {};
};

template <typename Arg_Type> struct Event : public EventInterface {
};

Then make your vector a std::vector<EventInterface *> or the like.

From a maintenance standpoint, it's not really that bad, because all the common interface class does is define a common interface for all events to adhere to. And if you did want to add new, non-template event classes, you could.

The downside is that you can't make ArgType part of the common interface. However, that makes a certain amount of sense: How else would you be able to legally invoke an arbitrary method on an Event* if you didn't know the types of the arguments?

Now you could require that all Arg_Type be subclasses of some common EventArgument class, and define your interface in terms of that. The template would still buy you something. It all depends on how you want to define your event interface.

Working example:

class EventArgument 
{
     public:
         virtual int getId () = 0;
         virtual ~EventArgument() { };
};

class EventInterface
{
    public:
        virtual void tweak  ( int t ) = 0;
        virtual void twiddle( int t ) = 0;
        virtual void frob   ( int f ) = 0;
        virtual EventArgument& getArg() = 0;

        virtual ~EventInterface() {};
};

class fredArg   : public EventArgument
{
    public:
    virtual int getId() { return 1; }
    ~fredArg() { };
};
class barneyArg : public EventArgument
{
    public:
    virtual int getId() { return 2; }
    ~barneyArg() { };
};

template <typename Arg_Type>
struct Event : public EventInterface
{

    Arg_Type arg;

    Event() { };

    virtual void tweak  ( int t ) { };
    virtual void twiddle( int t ) { };
    virtual void frob   ( int f ) { };

    virtual EventArgument& getArg()
    {
        return arg;
    }
};

#include <vector>
#include <iostream>

std::vector<EventInterface*> events;

int main()
{
    events.push_back( new Event<fredArg>(  ) );
    events.push_back( new Event<barneyArg>(  ) );

    std::cout << events[0]->getArg().getId() << std::endl;
    std::cout << events[1]->getArg().getId() << std::endl;
}

This prints 1 and 2 as you would expect.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top