Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

The Adapter Pattern


The Solution: A Mixture of Compile-time and Run-time Programming

First, you need a way to pass a type without passing an instance of the type, and you need a way to store that type and be able to use it afterwards. You also need a way to encapsulate an interface contract by defining only the signatures of the functions you will call and making them depend on the types that will be passed to you.

Passing Types Without Passing Instances

In the different libraries you have at your disposal, there are two that are very interesting for passing types around: loki, which contains a Type2Type class and Boost.MPL, which contains boost::mpl::identity. The latter suits our purposes a bit better because we'll be using the Boost MPL later on, and Boost.MPL has a coherent way of presenting meta-functions [2].

So, to pass a type to a function foo without passing an object of that type, the following code will work just fine:

#include <boost/mpl/identity.hpp>
template < typename Type >
void foo(const boost::mpl::identity< Type > &)
{
	/* do something with Type */
}
int main()
{
	foo(boost::mpl::identity< int >());
	return 0;
}

Here, main passes the type int to the function foo. The nice thing about this method is that no object is actually created: The compiler will use the boost::mpl::identity instance only to determine the type to use, but will optimize-away its instance (usually even in the lowest optimization settings[3]).

Determining and Implementing a Common Interface and Interface Contract

Most of our data-batch types implement a function that accepts a visitor, takes a reference to it as a parameter, and returns void. The type of the data batch and of the visitor change, as do the names of the functions that accept those visitors, but the "signature" described above is a common denominator that we can translate into code:

void (DataBatchType::*accept_visitor_)(VisitorType &)

is a pointer to a function in a class called DataBatchType that takes a reference to an instance of something of type VisitorType. The pointer itself is called accept_visitor_[4].

This means that if we have both types, we can, in theory, create a pointer to something that we can call with a visitor as a parameter. One problem is, though, that some data-batch types don't implement quite the same signature; for example, one of the data-batch types we have to support looks like this:

struct DataBatch
{
	Visitor & visit(Visitor & visitor)
	{
		/* do something with the visitor */
		return visitor;
	}
};

A straightforward approach to a solution for this problem would be to create a type trait for the data-batch type, as follows:

template <>
struct DataBatchTraits< DataBatch >
{
	static void acceptVisitor(DataBatch * data_batch, Visitor & visitor)
	{
		data_batch->visit(visitor);
	}
};

which is one way of implementing compile-time polymorphism: Conceptually, trait classes are much like compile-time interfaces to a given class. Though you can't create an instance of a trait class without knowing the complete type of the class that implements its interface, you don't have to know that while writing the code that uses the interface, as long as you've determined the interface you need when writing that code. We've determined that we needed something that returned void and takes a reference to a visitor as parameter, so our traits class will provide just that, while wrapping the actual implementation, which only the compiler needs to know at the right time.

This means that once we've determined a common interface for all data-batch types, we retain a certain flexibility on the data-batch implementation side (which we would not have if we had a common base class [5].This is especially useful as we're integrating into exisiting code which painfully lacks coherency but is also painfully cohesive.

However, just using a trait is not quite enough: We need to remember that the Task class' implementation will not know the type of the data-batch, and will therefore not be able to create an instance of a trait class, specialized for the type of the data-batch. We therefore need to return to run-time polymorphism and implement an Adapter class that exposes the common interface the traits will also expose. That Adapter class will look like this:

struct DataBatchAdapter
{
	virtual ~DataBatchAdapter();
	virtual void visit() = 0;
};

To use this class, a derived class will need to be implemented that uses the trait class discussed above. Once we've come to this conclusion, the solution is self-evident:

namespace Details
{
	template < typename DataBatchType, typename VisitorType >
	struct DataBatchAdapter : public ::DataBatchAdapter
	{
		DataBatchAdapter(DataBatchType * data_batch)
			: data_batch_(data_batch)
		{ /* no-op */ }
		virtual void visit()
		{
			VisitorType visitor(VisitorTraits< VisitorType >::create(data_batch_));
			DataBatchTraits< DataBatchType >::acceptVisitor(data_batch_, visitor);
		}
		DataBatchType * data_batch_;
	};
}

The following code will, therefore, create an instance of the adapter and call the visit function on it:

DataBatch data_batch;
std::auto_ptr< DataBatchAdapter > adapter(new Details::DataBatchAdapter< DataBatch, Visitor >(&data_batch));
adapter->visit();

Note, by the way, that this code explicitly uses a pointer in order to keep the run-time polymorphism possible.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.