So I've created an event driven architecture for my current program. I want each new event to have a number generated automatically with a function so I don't have to worry about overlap of id's and referencing is easy.

class Event
{
protected:
static int get_event_id();
public:
virtual int type() = 0;
};

class Event_Class
{
protected:
static int m_type;
char* msg;
int length;
public:
const char* get_msg();
int get_length(); 
int type(){return m_type;}
static void invoke(const char* msg);
//invoke this directly passes it to the event handler considering                                  //renaming but that isn't a big deal
};

//seperate .cpp file
int counter = 0;
int Event::get_event_id()
{
     return counter++;
}

Event_Class::m_type = Event::get_event_id();

Issues
1. "type" has to be defined as a static member with the name "type" (well technically no but I want the name to remain uniform, so that's why I find it bothersome) every time I inherit from the base event class. I'm not sure if there is another way to possibly fix this so it automatically makes a separate static int whenever I inherit from the base class with the same name. Each Event needs a different value, so declaring in the base class and inheriting isn't possible in any way I know.

The solution isn't near as elegant as it should be and I know there has to be a way to do something like this.

Recommended Answers

All 12 Replies

If you declare variable counter as a static member of class Event then there will be only one instance of that variable regardless of how many times class Event is used as the base class.

#include <iostream>
using std::cout;

// you could put these classes in a header file
class Event
{
public:
    static int counter;
protected:
    static int get_event_id() {return counter;}
public:
    virtual int type() = 0;
};

class A: public Event
{
public:
    A() 
    {
        Event::counter++;
        cout << "A::Event::type = " << get_event_id() << '\n';
    };
    virtual int type() {return 0;}
};

class B: public Event
{
public:
    B() 
    {
        Event::counter++;
        cout << "B::Event::type = " << get_event_id() << '\n';
    };
    virtual int type() {return 0;}
};

// The following must be in a *.cpp file
int Event::counter = 0;

int main()
{
    A a;
    B b;
}

Perhaps you are looking for a const member rather than a static member? Something like:

#include <iostream>

struct Event {
    static int next_id;
    const int eid;
    Event () : eid(++Event::next_id) {}
};

int Event::next_id = 0;

int main ()
{
    Event e1, e2, e3, e4;
    std::cout << "e1: " << e1.eid << std::endl;
    std::cout << "e2: " << e2.eid << std::endl;
    std::cout << "e3: " << e3.eid << std::endl;
    std::cout << "e4: " << e4.eid << std::endl;
}

Realize that using the static member here as a counter is just me being lazy. The initialization of the const member can come from any source you desire.

But I want it to change so that if I made another 'A' class such as A a and A b they would have the same id. You example makes them different. All A's need to have the same id and all B's need the same id. There could be several of a single type made in a single frame.

I don't mind doing my solution just thought there probably is a better way to do it.

It needs to be inherited so I can specialize the event so I can make an event pass a msg to something. Example: Collision detected between an ally and enemy. I proc an event "Collision" with the id of the ally and enemy and the outcome. If I have 100 items listed to respond to a collision or whatnot the event handler has a 2D array where the first dimension is indexed by the Collision event id. It than iterates through all 100 items listed in the second dimension and passes the event along to each so they can verify if they care about that collision and if so use the data within it to make the desired changes.

This is useful because I might have health bar, energy bar, and several screen effects linked to the characters health, but are independent objects. These need to be notified to update their values each frame and/or be triggered. Otherwise, I need to have each item programmed to independently check their dependencies and none of that code is very portable.

Can you use templates?

#include <iostream>

template< class T >
struct Event {
    static int eid;
    Event () { ++eid; }
};

template< class T > int Event< T >::eid = 0;

class Foo;
class Bar;

int main ()
{
    Event< Foo > f1, f2, f3;
    Event< Bar > b1, b2;
    std::cout << "f1 : " << f1.eid << std::endl;
    std::cout << "f2 : " << f2.eid << std::endl;
    std::cout << "f3 : " << f3.eid << std::endl;
    std::cout << "b1 : " << b1.eid << std::endl;
    std::cout << "b2 : " << b2.eid << std::endl;
    return 0;
}

The output of that is:

f1 : 3
f2 : 3
f3 : 3
b1 : 2
b2 : 2

Or, if that doesn't fit what you are trying to do, maybe keep the static member as part of the derived class rather than the base:

struct Event {};

struct Foo : Event {
    static int eid;
    Foo () { ++eid; }
};

struct Bar : Event {
    static int eid;
    Bar () { ++eid; }
};

int Foo::eid = 0;
int Bar::eid = 0;

Ya think your second solution is similar to what I'm doing and probably what I'll have to do. Thanks for the help.

Your previous one only stays constant and different because of how you declare a different number of variables. So the number of variables made = the id number which isn't what I desire.

Both examples achieve the same effect. The fact that I put the increment operator in the constructor was merely to show the change. You could theoretically make that change anywhere you like and it would be visible by all instances of the class.

Another option is to create a hash given the class information.

#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;

size_t hash(const std::string &data) {
  size_t h = 0;
  for (unsigned i=0; i < data.size(); i++){
    h = (h << 6) ^ (h >> 26) ^ data[i];
  }
  return h;
}

template<class Class>
size_t getEventID(const Class& c){ return hash( typeid(c).name()); }

class Foo{};
class Bar{};

int main(){
  Foo f1,f2;
  Bar b1,b2;
  std::string str1,str2;
   std::cout << "f1 : " << getEventID(f1) << std::endl;
   std::cout << "f2 : " << getEventID(f2) << std::endl;
   std::cout << "b1 : " << getEventID(b1) << std::endl;
   std::cout << "b2 : " << getEventID(b2) << std::endl;
   std::cout << "str : " << getEventID(str1) << std::endl;
   std::cout << "str : " << getEventID(str2) << std::endl;
}

although you might want to automate this process

What's wrong with simply using the address of m_type as the unique ID you want?

I think the OP is looking to modify the value at runtime and have the effect propagate (through the static mechanism). Taking the address would be unique but immutable.

I think the OP is looking to modify the value at runtime

Why? Reading the original post, I gather that the OP is trying to do something like this:

#include <iostream>
using namespace std;

struct AbstractEvent
{
    static int counter;

    static int get_next_id();

    static void init_event_ids();

    virtual int get_type() = 0;

    virtual ~AbstractEvent() {}
};

struct ConcreteEventA : AbstractEvent { static int type; int get_type() { return type; } };
struct ConcreteEventB : AbstractEvent { static int type; int get_type() { return type; } };
struct ConcreteEventC : AbstractEvent { static int type; int get_type() { return type; } };

// more concrete events ...

int AbstractEvent::counter = 0;
int AbstractEvent::get_next_id() { return counter++; }

int ConcreteEventA::type = AbstractEvent::get_next_id();
int ConcreteEventB::type = AbstractEvent::get_next_id();
int ConcreteEventC::type = AbstractEvent::get_next_id();

// more concrete event type initializations  ...

int main()
{
    ConcreteEventA a1, a2;
    ConcreteEventB b1, b2;
    ConcreteEventC c1, c2;

    AbstractEvent & ea1 = a1, & ea2 = a2;
    AbstractEvent & eb1 = b1, & eb2 = b2;
    AbstractEvent & ec1 = c1, & ec2 = c2;

    cout << ea1.get_type() << endl;
    cout << eb1.get_type() << endl;
    cout << ec1.get_type() << endl;

    cout << boolalpha << endl;

    cout << (ea1.get_type() == ea2.get_type()) << endl;
    cout << (ea1.get_type() == eb2.get_type()) << endl;
}

You can achieve the same functionality simply doing this:

#include <iostream>
using namespace std;

struct AbstractEvent
{
    virtual const void * get_type() = 0;

    virtual ~AbstractEvent() {}
};

struct ConcreteEventA : AbstractEvent { const void * get_type() { static const int type = 0; return &type; } };
struct ConcreteEventB : AbstractEvent { const void * get_type() { static const int type = 0; return &type; } };
struct ConcreteEventC : AbstractEvent { const void * get_type() { static const int type = 0; return &type; } };

// more concrete events ...

int main()
{
    ConcreteEventA a1, a2;
    ConcreteEventB b1, b2;
    ConcreteEventC c1, c2;

    AbstractEvent & ea1 = a1, & ea2 = a2;
    AbstractEvent & eb1 = b1, & eb2 = b2;
    AbstractEvent & ec1 = c1, & ec2 = c2;

    cout << ea1.get_type() << endl;
    cout << eb1.get_type() << endl;
    cout << ec1.get_type() << endl;

    cout << boolalpha << endl;

    cout << (ea1.get_type() == ea2.get_type()) << endl;
    cout << (ea1.get_type() == eb2.get_type()) << endl;
}

The only problem I see with the second approach is that the OP wants to use the event
IDs to index an array. But this is not a real problem, as he could simply switch to a map.

Why? Reading the original post, I gather that the OP is trying to do something like this:

#include <iostream>
using namespace std;

struct AbstractEvent
{
    static int counter;

    static int get_next_id();

    static void init_event_ids();

    virtual int get_type() = 0;

    virtual ~AbstractEvent() {}
};

struct ConcreteEventA : AbstractEvent { static int type; int get_type() { return type; } };
struct ConcreteEventB : AbstractEvent { static int type; int get_type() { return type; } };
struct ConcreteEventC : AbstractEvent { static int type; int get_type() { return type; } };

// more concrete events ...

int AbstractEvent::counter = 0;
int AbstractEvent::get_next_id() { return counter++; }

int ConcreteEventA::type = AbstractEvent::get_next_id();
int ConcreteEventB::type = AbstractEvent::get_next_id();
int ConcreteEventC::type = AbstractEvent::get_next_id();

// more concrete event type initializations  ...

int main()
{
    ConcreteEventA a1, a2;
    ConcreteEventB b1, b2;
    ConcreteEventC c1, c2;

    AbstractEvent & ea1 = a1, & ea2 = a2;
    AbstractEvent & eb1 = b1, & eb2 = b2;
    AbstractEvent & ec1 = c1, & ec2 = c2;

    cout << ea1.get_type() << endl;
    cout << eb1.get_type() << endl;
    cout << ec1.get_type() << endl;

    cout << boolalpha << endl;

    cout << (ea1.get_type() == ea2.get_type()) << endl;
    cout << (ea1.get_type() == eb2.get_type()) << endl;
}

You can achieve the same functionality simply doing this:

#include <iostream>
using namespace std;

struct AbstractEvent
{
    virtual const void * get_type() = 0;

    virtual ~AbstractEvent() {}
};

struct ConcreteEventA : AbstractEvent { const void * get_type() { static const int type = 0; return &type; } };
struct ConcreteEventB : AbstractEvent { const void * get_type() { static const int type = 0; return &type; } };
struct ConcreteEventC : AbstractEvent { const void * get_type() { static const int type = 0; return &type; } };

// more concrete events ...

int main()
{
    ConcreteEventA a1, a2;
    ConcreteEventB b1, b2;
    ConcreteEventC c1, c2;

    AbstractEvent & ea1 = a1, & ea2 = a2;
    AbstractEvent & eb1 = b1, & eb2 = b2;
    AbstractEvent & ec1 = c1, & ec2 = c2;

    cout << ea1.get_type() << endl;
    cout << eb1.get_type() << endl;
    cout << ec1.get_type() << endl;

    cout << boolalpha << endl;

    cout << (ea1.get_type() == ea2.get_type()) << endl;
    cout << (ea1.get_type() == eb2.get_type()) << endl;
}

The only problem I see with the second approach is that the OP wants to use the event
IDs to index an array. But this is not a real problem, as he could simply switch to a map.

Nice idea. Although there isn't no need for initialization for the static const int type.

Hm...I'll have to add that to my bag of tricks using a static member as an identifier as that could be useful in a lot of situations. However, for this purpose it isn't ideal. Referencing with a simple array and int allows me to find the proper array of connected items with a simple offset rather than potentially hundreds of extra comparisons per frame.

Kinda my pet project and so being a bit more of a performance stickler than I normally would be.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.