<!DOCTYPE article
  PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
  "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
<article>

	<articleinfo>
		<title>Templatized Visitor Pattern</title>
		<author>
			<firstname>Michael</firstname>
			<surname>Smit</surname>
		</author>
		<copyright><year>2006</year></copyright>
	</articleinfo>
	
	<section><title>License</title>
	<para>This document is licensed under a Creative Commons <ulink url="http://creativecommons.org/licenses/by/2.5/">license</ulink>.</para>
	</section>
	<section><title>What's the problem?</title>
	<para>C++ lacks introspection. There are a number of reasons this can be a
	problem and in my particular case I was trying to address two issues:
	<orderedlist>
		<listitem><para>Binary serialization of an arbitrary data type to a particular byte-order scheme.</para></listitem>
		<listitem><para>Automatic human-readable text description of an arbitrary data type.</para></listitem>
	</orderedlist>
	</para>
	
	<para>There are a number of frameworks that already address this problem, but
	I happen to work in a real-time environment so we end up re-writing a lot of
	stuff anyway and there are some similarities between the two tasks that
	make for a nice solution. First lets describe the basic idea as it applies
	to serialization.</para>
	</section>
	
	<section><title>Serialization</title>
	<para>A basic solution to the serialization problem is essentially the
	visitor pattern. When working purely with objects, this involves two 
	abstract classes:
	<varname>ISerializable</varname> and <varname>IArchive</varname>. 
	<varname>IArchive</varname> is the visitor and <varname>ISerializable</varname>
	is the acceptor.
	<programlisting>
	class IArchive
	{
	public:
		virtual ~IArchive();
		
		//This may either put a value into the field
		//or take the value out
		virtual void op(int &amp;value) = 0;
		virtual void op(unsigned int &amp;value) = 0;
		//etc
		
		//Structured data type
		virtual void op(ISerializable &amp;value) = 0;
	};
	
	class ISerializable
	{
	public:
		virtual ~ISerializable();
		
		virtual void accept(IArchive &amp;archive);
	};
	</programlisting>
	</para>
	
	<para><varname>ISerializable</varname> accepts <varname>IArchive</varname>
	and the reports each sub-item via the <methodname>op</methodname> method.</para>
	
	<section><title>Handling Structures</title>
	<para>This is great when all you want to serialize are objects and all
	those objects implement <classname>ISerializable</classname>, but what about
	structures?
	</para>
	<para>Assuming the structures have an <methodname>accept</methodname> method
	then all we need to do is create a template.
<programlisting>
	class IArchive
	{
	public:
		virtual ~IArchive();
		
		template&lt;class Type>
		void op(Type &amp;value);
		
		//This may either put a value into the field
		//or take the value out
		virtual void _op(int &amp;value) = 0;
		virtual void _op(unsigned int &amp;value) = 0;
		//etc
		
		//Structured data type
		virtual void _op(ISerializable &amp;value) = 0;
	};
	
	//specialize int, unsigned int, and ISerializable
	template&lt;>
	void op(int &amp;value);
	template&lt;>
	void op(unsigned int &amp;value);
	template&lt;>
	void op(ISerializable &amp;value);
</programlisting>
	</para>
	
	<para>We ints, unsigned ints, and <classname>ISerializable</classname>s the
	same way as before. Assuming anything else is a structure/object with an
	<methodname>accept</methodname> method then all we have to do it as an <classname>ISerializable</classname>
	and pass it to the <methodname>_op</methodname> method.
<programlisting>
	template&lt;class Type>
	class SerializableWrapper:public SerializableWrapper
	{
	public:
		SerializableWrapper(Type *wrapValue);
		
		virtual void accept(IArchive &amp;archive);
	private:
		Type *wrapValue;
	};
	
	template&lt;class Type>
	SerializableWrapper::SerializableWrapper(Type *wrapValue)
	{
		this->wrapValue = wrapValue;
	};
	
	/*Assuming that Type has an accept method*/
	template&lt;class Type>
	SerializableWrapper::accept(IArchive &amp;archive)
	{
		wrapValue->accept(archive);
	};
	
	/*Use this class to handle the generic case for IArchive*/
	template&lt;class Type>
	void IArchive::op(Type &amp;value)
	{
		SerializableWrapper&lt;Type> wrapper(&amp;value);
		_op(wrapper);
	};
</programlisting>
	</para>
	</section>
	<section>
	<title>Handling 3rd Party Data Types</title>
	<para>This works pretty well except it still assumes the data type being
	serialized has an <methodname>accept</methodname> method. To handle this
	we refactor <methodname>SerializableWrapper::accept</methodname> to
	call a template function which by default calls <methodname>accept</methodname>,
	but can be specialized to handle particular cases.
<programlisting>
	template&lt;class Type>
	void serializeType(IArchive &amp;archive, Type &amp;value)
	{
		value.accept(archive);
	};

	/*Assuming that Type has an accept method*/
	template&lt;class Type>
	SerializableWrapper::accept(IArchive &amp;archive)
	{
		serializeType(archive, *wrapValue);
	};
</programlisting>
	</para>
	
	<para>Now to handle a generic structure you do the following:
<programlisting>
	template&lt;>
	void serializeType(IArchive &amp;archive, MyStructure &amp;str)
	{
		archive.op(str.a);
		archive.op(atr.b);
		....
	};
</programlisting>
	</para>
	</section>
	</section>
	<section><title>Generalizing the pattern</title>
	<para>The problem is we are somewhat restricted
	in terms of what we can put into the serialization. We don't have names for the
	various types, so it would be difficult to do a nice XML serialization. We
	also don't have the ability to specify bit fields. If you want to create
	something like an IP header this might be a bit annoying. Finally, what
	if I want something else like long descriptions for each subtype?</para>
	
	<para>I could add each of the attributes as arguments to the <methodname>_op</methodname>
	methods, but that would lead to some other problems. First of all I would
	have to provide this information for all data types that support the framework
	even though only some would use them. Second, every archive class would
	have to figure out what to ignore. As a result not only would everyone have
	to do a lot more typing, it would now become unclear what objects support
	which features</para>
	
	<para>The solution, as always, is another template.
<programlisting>
template&lt;class Information>
class AbstractVisitor
{
public:
	virtual ~AbstractVisitor();
	
	template&lt;class Type>
	void op(Type &amp;value, const Information &amp;info);
	
	...
};

template&lt;class Information>
class IVisitable
{
public:
	virtual ~IVisitable();
	
	virtual void accept(AbstractVisitor &amp;visitor) = 0;
};

template&lt;class Visitor, class Type>
void visitorTypeBehavior(Visitor &amp;visitor, Type &amp;value)
{
	value.accept(visitor);
}
</programlisting>
	</para>
	
	<para>I leave the implementation details up to you, but let's see some
	examples of what I can do. First of all, defining a new visitor/visitable
	pair is easy:
<programlisting>
typedef AbstractVisitor&lt;DocumentationInfo> AbstractDocumentationVisitor;
typedef IVisitable&lt;DocumentationInfo> IDocumentable;

typedef AbstractVisitor&lt;SerializationInfo> AbstractSerializationVisitor;
typedef IVisitable&lt;SerializationInfo> ISerializable;
</programlisting>
	</para>
	<para>Second, I can now support only the information I want for a particular
	type.
<programlisting>
/*only support documentation*/
template&lt;>
visitorTypeBehavior(AbstractDocumentationVisitor &amp;visitor, MyDocumentable &amp;doc)
{
	...
};

/*support both types of visitor*/
template&lt;>
visitorTypeBehavior(AbstractDocumentationVisitor &amp;visitor, MyVersatile &amp;v)
{
	...
};

template&lt;>
visitorTypeBehavior(AbstractSerializationVisitor &amp;visitor, MyVersatile &amp;v)
{
	...
};

/*support both types right in the struct*/
struct MyStruct
{
	void accept(AbstractDocumentationVisitor &amp;visitor);
	void accept(AbstractSerializationVisitor &amp;visitor);
	...
};
</programlisting>	
	</para>
	</section>
</article>
