While working on a utility that may perhaps evolve into the next version of the Engine, written primarily in JavaScript, I ran into a small problem when I tried to wrap output files for use in JavaScript.
v8, when it “goes away” at program end, does not collect its garbage. Not usually a problem, as most of the time, the operating system will clean up after you. But I didn’t need the memory cleared. I needed a destructor called.
The output file wrapper was wrapping a class which wrapped ostream. The standard library stream usually uses a buffer, and only calling flush() or destroying the stream flushes it to the actual output. I needed this to happen automatically, so I wrote up a simple template class, AutoDestroy, along with a helper function, autoDestroy.
I call it roughly like this, but with smart pointers and other code mixed in:
//create a stream OutputStream *stream = new OutputStream(); //set auto destroy autoDestroy(stream); //stream is deleted delete stream; //cancel auto destroy cancelAutoDestroy(stream);
The code itself is pretty simple. There are probably some ways that are even simpler, more elegant, and take fewer lines of code, but this does seem to work:
/* * This template class is a very simple class which allows registration of items * needing destruction. * * Very simple: a set of items to destroy, and a map to keep track of how many * destruction references there are (allowing add and remove to be called * more than once). * * It is split into two parts (the AutoDestroy class and Destroyer) so that * the actual destruction call can be overridden (by specializing Destroyer). */ #include <set> #include <map> namespace Shell { template <typename T> class Destroyer { public: Destroyer(T *what) { this->ptr = what; } void destroy() { delete ptr; } protected: T *ptr; }; template <typename T> class AutoDestroy { public: static void Add(T *what) { Instance()->add(what); } static void Remove(T *what) { Instance()->remove(what); } virtual ~AutoDestroy() { typename std::set<T *>::iterator i; for (i = autoDestroy.begin(); i != autoDestroy.end(); i++) { Destroyer<T> destroyer(*i); destroyer.destroy(); } } static AutoDestroy *Instance() { static AutoDestroy instance; return &instance; } protected: std::map<T *, int> references; std::set<T *> autoDestroy; void add(T *what) { //if it doesn't exist yet, add it. if (autoDestroy.count(what) < 1) { //insert into auto destroy list autoDestroy.insert(what); //set reference to 1 references[what] = 1; //return because references is already 1 return; } //increment reference references[what]++; } void remove(T *what) { //if it doesn't exist, return if (autoDestroy.count(what) < 1) return; //decrement references, and if there aren't any, remove from list. if (--references[what] <= 0) { autoDestroy.erase(what); references.erase(what); } } }; /* Helper functions to make things dead simple */ template<typename T> void autoDestroy(T *what) { AutoDestroy<T>::Add(what); } template<typename T> void cancelAutoDestroy(T *what) { AutoDestroy<T>::Remove(what); } }