I'm trying to make a sanely-usable implementation of my events system. In order to identify event types, I have what I call a "type path,' which identifies the path to an event type through the hierarchy. This way, I can handle, for example, all InputEvents at one place whether they're key presses, mouse input, or whatever else. A sticky issue, though, has been giving event types their identities. What I've most recently done is do this by having each instance retrieve a leaf identity from a static member function of the Event class, which serves simply as an interface other than performing this function. However, the simplest way to ensure that each type has exactly one identity within this structure seemed to be to use maps based on type paths (up to but excluding the leaf identity/identifier) and typeid().hash_code().
Specifically, what I want to have is a system to which events can be added easily without having to look up a bunch of information or perform a lot of silly boilerplate crap. Considering this (and possibly things I'm not realizing I should want?),
Fairly simple example of what I have now:
#include <iostream>
#include <vector>
#include <typeinfo>
#include <map>
void spew(std::vector<unsigned int> vect) { for (unsigned int i=0;i<vect.size();++i) std::cout << vect.at(i) << ","; std::cout << std::endl; }
class Foo
{
public:
Foo() {}
virtual ~Foo() {}
static unsigned int getSubtype(std::vector<unsigned int> typePath, Foo *evt)
{
static std::map<std::vector<unsigned int>, std::map<std::size_t, unsigned int> > typeMap;
std::size_t typehash = typeid(*evt).hash_code();
if (typeMap.find(typePath) == typeMap.end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
else
{
if (typeMap[typePath].find(typehash) == typeMap[typePath].end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
return typeMap[typePath][typehash];
}
}
virtual void test() { std::cout << "Foo" << std::endl; }
protected:
std::vector<unsigned int> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Bar() {}
virtual void test() { std::cout << "Bar: "; spew(m_typePath);}
};
class Baz : public Foo
{
public:
Baz()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Baz() {}
virtual void test() { std::cout << "Baz: "; spew(m_typePath);}
};
class Qux : public Baz
{
public:
Qux()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Qux() {}
virtual void test() { std::cout << "Qux: "; spew(m_typePath);}
};
int main()
{
Foo foo0;
std::cout << "----" << std::endl;
Bar bar0;
std::cout << "----" << std::endl;
Baz baz0;
std::cout << "----" << std::endl;
Qux qux0;
}
Output:
----
Bar: 0,
----
Baz: 1,
----
Baz: 1,
Qux: 1,0,
This and other tests exhibit the desired behavior, to be clear.
Edit: Previous title didn't really match what I mean to ask.
Possibly relevant notes: This is meant for part of a library, and a highly parallel one at that. I've omitted code relevant to concurrency for simplicity of representing the design, but it may be that such information would be useful for design purposes as well. Also note that I'm still only asking for help with creating/assigning type identifiers; I mention these because some designs may not be applicable given their implied constraints.
Win edit: Well, I have an implementation that's ridiculously fast and does exactly what I need. With a few derived classes, I can instantiate ten million per ~thread(I added in TBB for some other tests; it may or may not use exactly eight threads however it pleases) spread across the derived classes, each having two or more elements in its path, in typically well under .02s. Original implementation managed about four or five seconds depending on containers and such and was just silly. Result (enough to get the idea, anyway):
template<typename T> class EventID
{
public:
static const std::size_t typeID;
};
template<typename T> const std::size_t EventID<T>::typeID = typeid(T).hash_code();
class Foo
{
public:
Foo()
{
m_typePath.push_back(EventID<Foo>::typeID);
}
protected:
neolib::vecarray<std::size_t, 100, neolib::nocheck> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(EventID<Bar>::typeID);
}
};
See final edit to original post for solution.