All these suggestions are helpful, thank you!
I came up with a solution like this. Using typeid
was not really necessary, so I decided to index each Wire by name. I tried using the std::any to eliminate WireBase
but could not get the right cast magic to work.
The (templated) Meyers singleton would work too, except that I want to be able to delete a Hub and make everything go away. I am effectively using a bunch of singletons, but want the application to be able to reset to the initial state.
class Hub
{
public:
template<class T>
Wire<T>* get_wire (std::string name)
{
WireBase *result = wires[name];
if (result == nullptr)
{
result = new Wire<T>();
wires[name] = result;
}
return static_cast<Wire<T>*>(result);
}
private:
std::map<std::string, WireBase*> wires;
};
The Wire class looks something like this:
template<typename T>
class Wire: public WireBase
{
public:
void publish (const T &message)
{
for (std::function<void (const T& message)> &handler : subscribers)
{
handler(message);
}
}
void subscribe (std::function<void (const T&)> &handler)
{
subscribers.push_back(handler);
}
private:
std::vector<std::function<void (const T&)>> subscribers;
};
With a Demo function:
void Demo::execute ()
{
std::cout << "Starting demo" << std::endl;
Hub hub;
std::cout << "Hub " << hub << std::endl;
Wire<Payload1> *w1 = hub.get_wire<Payload1>("w1");
Wire<Payload2> *w2 = hub.get_wire<Payload2>("w2");
std::cout << "W1 " << w1 << std::endl;
std::cout << "W2 " << w2 << std::endl;
std::function<void (const Payload1&)> foo1 = [] (const Payload1 &p)
{
std::cout << "Foo1 " << p.get() << std::endl;
};
std::function<void (const Payload2&)> foo2 = [] (const Payload2 &p)
{
std::cout << "Foo2 " << p.get() << std::endl;
};
w1->subscribe(foo1);
w2->subscribe(foo2);
Payload1 p1;
Payload2 p2;
w1->publish(p1);
w2->publish(p2);
std::cout << "Ending demo" << std::endl;
}
Starting demo
Hub #[Hub]
W1 #[Payload1>]
W2 #[Payload2>]
Foo1 Payload1
Foo2 Payload2
Ending demo