create blog

go home go home
  1. about
  2. code
  3. wiki
  4. blog

v8, Application Exit, and Destructors

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);
	}
}

Leave a Reply