This question is long so please bear with me.
I'm attempting to solve a dilemma I'm having with memory management, shared pointers, and maps. I just wanted to get some feedback about my architecture, maybe some of ya'll have done it better in the past.
The following examples will be done is pseudo code.
I have a listener class:
class MyListener {
friend class Command;
public:
MyListener() {}
virtual ~MyListener() {}
void handleUpdate() {
std::cout << "Update Handled" << std::endl;
}
};
Which is to be evoked everytime an object update is called. I'm using a middleware called OpenDDS for interprocess communication framework.
I have a Command class, which inherits a DDS object, and leverages the on_data_received(). When on_data_received() is evoked, I want to call the handleUpdate() method from the class above.
class Command {
public:
/*standard constructor destructor here*/
void on_data_received() {
m_listener->handleUpdate();
}
void write();
private:
MyListener *m_listener;
};
Herein lies the problem. The class that manages this is a Singleton, and uses two methods publish and subscribe to either publish a DDS message or subscribe to one. The subscribe method takes a key value and a raw pointer.
The singleton manages a
std::map<std::string name, Command>
In which a Command class contains the MyListener class.
Here is a snippet of pseudo code that breaks it:
class TaterTotListener : public MyListener {
void handleCommand() {
std::cout << "Tater tot found" << std::endl;
}
};
int main() {
// make a new smart pointer to the listener
boost::shared_ptr<TaterTotListener> ttl(new TaterTotListener);
// tell the singleton we want to publish an object called "TaterTot"
CommandManager::instance()->publish("TaterTot");
// tell the singleton we want to subscribe to an object called tater tot
CommandManager::isntance()->subscribe("TaterTot", ttl.get());
// processing goes here
// deallocation
}
Upon deallocation, boost is removing it's ownership of the shared pointer. CommandManager attempts to "clean up" by removing all objects named "TaterTot", but since boost::shared_ptr has already cleaned itself up, a double free memory corruption is thrown. CommandManager singleton is always cleaned up last, so declaring a raw pointer and passing to the subscribe method is going to result in the same behaviour.
Any ideas out there? Did I miss something obvious and intuitive? Am I misunderstanding the usage of shared pointers in this instance?
Any help is greatly appreciated. I'll buy ya'll a beer.
Your design mixes two well-known design patterns, Observer and Command.
Observer defines a one-to-many dependency between objects so that when one object changes state, all its dependent clients are notified and updated automatically.
Command encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
I would recommend studying those patterns (see the links above) and refactor your design to separate the encapsulation of requests from the observation of those requests.