Click here to Skip to main content
15,896,453 members
Articles / Programming Languages / C++11

Implementing COM Interfaces with C++0x Variadic Templates

Rate me:
Please Sign up or sign in to vote.
4.92/5 (25 votes)
4 Sep 2011CPOL11 min read 43.1K   662   35  
A step-by-step illustration of a minimalistic pattern for implementing a series of COM interfaces with little code
#pragma once

/*! \mainpage
 * 
 * \section License
 * 
 * Copyright (c) 2011, KO Software (official@ko-sw.com)
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     - All original and modified versions of this source code must include the
 *       above copyright notice, this list of conditions and the following
 *       disclaimer.
 *
 *     - The name of KO Software may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY KO SOFTWARE ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT WILL KO SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <utility>

////////////////////////////////////////////////////////////////////////////////

//! \def SUPPORTED_CPP0X
//! When set to nonzero, C++0X code with variadic templates will be present.

#ifdef __GXX_EXPERIMENTAL_CXX0X__
#  define SUPPORTED_CPP0X 1
#else
#  define SUPPORTED_CPP0X 0
#endif //__GXX_EXPERIMENTAL_CXX0X__

////////////////////////////////////////////////////////////////////////////////

//! \def SUPPORTED_UUIDOF
//! When set to nonzero, assumes that the compiler can provide a built-in
//! __uuidof operator.

#ifdef _MSC_EXTENSIONS
#  define SUPPORTED_UUIDOF 1
#else
#  define SUPPORTED_UUIDOF 0
#endif //_MSC_EXTENSIONS

////////////////////////////////////////////////////////////////////////////////

#if (SUPPORTED_UUIDOF == 0)

template<typename t>
struct hold_uuidof { static GUID __IID; };

#  define DECLSPEC_NOVTABLE

#  define DEFINE_UUIDOF_ID(Q, IID) template<> GUID hold_uuidof<Q>::__IID = IID;
#  define DEFINE_UUIDOF(Q) DEFINE_UUIDOF_ID(Q, IID_##Q)
#  define __uuidof(Q) hold_uuidof<Q>::__IID

#endif	// (SUPPORTED_UUIDOF != 0)

//! \brief
//! Provides discovery information for a supported COM interface.
//! 
//! \tparam tInterface     The type of the interface being discovered.
//! 
//! \tparam tIID           The interface ID of \a tInterface.
template<typename tInterface, LPCGUID tIID = &__uuidof(tInterface)>
class DECLSPEC_NOVTABLE ComEntryDiscoverable
{
public:
	typedef tInterface Interface;

public:
	//! \brief
	//! Represents the main method for interface discovery infrastructure.
	//! 
	//! \remarks
	//! This method is called internally by the ComInstance class when its
	//! QueryInterface method is invoked.
	template<typename tObject>
	static HRESULT InternalQueryInterface(REFIID theIID, tObject * theObj, void ** theOut)
	{
		if (theIID == *tIID)
			*theOut = static_cast<tInterface *>(theObj);
		else
			return E_NOINTERFACE;

		return S_OK;
	}
};


//! \brief
//! Implements exactly one COM interface and provides discovery
//  information for it.
template<typename tInterface, LPCGUID tIID = &__uuidof(tInterface)>
class DECLSPEC_NOVTABLE ComEntry : public tInterface, public ComEntryDiscoverable<tInterface, tIID>
{ };


//! \brief
//! Provides discovery information for a parent COM interface that is common
//! between two or more child COM interfaces being implemented.
//! 
//! \tparam tInterface     The type of the interface being discovered.
//! 
//! \tparam tIntermediate  The interface that is a child of \a tInterface that
//!                        can serve as a hint for resolving casting ambiguities.
//! 
//! \tparam tIID           The interface ID of \a tInterface.
//! 
//! \remarks
//! This class is necessary for providing discovery information about IUnknown
//! since the latter is parent for all COM interfaces. In such cases where direct
//! cast to IUnknown is not possible, two-step casting is necessary.
template<typename tInterface, typename tIntermediate, LPCGUID tIID = &__uuidof(tInterface)>
class DECLSPEC_NOVTABLE ComEntry2StepDiscoverable
{
public:
	typedef tInterface Interface;

protected:
	template<typename tObject>
	static HRESULT InternalQueryInterface(REFIID theIID, tObject * theObj, void ** theOut)
	{
		if (theIID == *tIID)
			*theOut = static_cast<tInterface *>( static_cast<tIntermediate *>(theObj) );
		else
			return E_NOINTERFACE;

		return S_OK;
	}
};


//! \brief
//! Represents a terminating block for interface discovery, allowing for
//! discovery of IUnknown.
//! 
//! \tparam tIntermediate  Any interface that implements IUnknown.
template <typename tIntermediate>
class DECLSPEC_NOVTABLE ComEntryTerminator :
	public ComEntry2StepDiscoverable<IUnknown, tIntermediate>
{ };

//! \brief
//! Represents a compound COM entry that can implement n COM interfaces and
//! contain discovery information about (n+1) COM interfaces where the last interface
//! must always be IUnknown.
//! 
//! \tparam tEntry1        The first COM entry.
//! \tparam tEntry2        Second COM entry. If unneeded, a terminating entry
//!                        is used, providing discovery of IUnknown.
//! \remarks
//! In order to implement more than one COM interface,
//! this class can be daisy-chained in the \a tEntry2 template argument.
template <typename tEntry1, typename tEntry2 = ComEntryTerminator<typename tEntry1::Interface> >
class DECLSPEC_NOVTABLE ComEntryCompound : public tEntry1, public tEntry2
{
public:
	template<typename tObject>
	static HRESULT InternalQueryInterface(REFIID theIID, tObject * theObj, void ** theOut)
	{
		HRESULT aRes = tEntry1::InternalQueryInterface(theIID, theObj, theOut);

		if ( FAILED(aRes) )
			aRes = tEntry2::InternalQueryInterface(theIID, theObj, theOut);

		return aRes;
	}
};


////////////////////////////////////////////////////////////////////////////////


//! \brief
//! Implements exactly one COM interface and provides discovery information
//! about this interface, plus IUnknown.
//! 
//! \tparam tInterface1    The interface being implemented.
template <typename tInterface1>
class DECLSPEC_NOVTABLE ComEntry1 :
	public ComEntryCompound
					<
						ComEntry<tInterface1>
					>
{ };


//! \brief
//! Implements exactly two COM interfaces and provides discovery information
//! about these interfaces, plus IUnknown.
//! 
//! \tparam tInterface1    The first interface being implemented.
//! 
//! \tparam tInterface2    The second interface being implemented.
template <typename tInterface1, typename tInterface2>
class DECLSPEC_NOVTABLE ComEntry2 :
	public ComEntryCompound
					<
						ComEntry<tInterface1>,
						ComEntry1<tInterface2>
					>
{ };


//! \brief
//! Implements exactly three COM interfaces and provides discovery information
//! about these interfaces, plus IUnknown.
//! 
//! \tparam tInterface1    The first interface being implemented.
//! 
//! \tparam tInterface2    The second interface being implemented.
//! 
//! \tparam tInterface3    The third interface being implemented.
template <typename tInterface1, typename tInterface2, typename tInterface3>
class DECLSPEC_NOVTABLE ComEntry3 :
	public ComEntryCompound
					<
						ComEntry<tInterface1>,
						ComEntry2<tInterface2, tInterface3>
					>
{ };


//! \brief
//! Implements exactly four COM interfaces and provides discovery information
//! about these interfaces, plus IUnknown.
//! 
//! \tparam tInterface1    The first interface being implemented.
//! 
//! \tparam tInterface2    The second interface being implemented.
//! 
//! \tparam tInterface3    The third interface being implemented.
//! 
//! \tparam tInterface4    The fourth interface being implemented.
template <typename tInterface1, typename tInterface2, typename tInterface3, typename tInterface4>
class DECLSPEC_NOVTABLE ComEntry4 :
	public ComEntryCompound
					<
						ComEntry<tInterface1>,
						ComEntry3<tInterface2, tInterface3, tInterface4>
					>
{ };


//! \brief
//! Implements exactly five COM interfaces and provides discovery information
//! about these interfaces, plus IUnknown.
//! 
//! \tparam tInterface1    The first interface being implemented.
//! 
//! \tparam tInterface2    The second interface being implemented.
//! 
//! \tparam tInterface3    The third interface being implemented.
//! 
//! \tparam tInterface4    The fourth interface being implemented.
//! 
//! \tparam tInterface5    The fifth interface being implemented.
template <typename tInterface1, typename tInterface2, typename tInterface3, typename tInterface4, typename tInterface5>
class DECLSPEC_NOVTABLE  ComEntry5 :
	public ComEntryCompound
					<
						ComEntry<tInterface1>,
						ComEntry4<tInterface2, tInterface3, tInterface4, tInterface5>
					>
{ };

////////////////////////////////////////////////////////////////////////////////

// Variadic-template versions

#if (SUPPORTED_CPP0X != 0)

//! \brief
//! Implements an arbitrary number of COM interfaces and provides discovery
//! information about these interfaces, plus IUnknown.
//! 
//! \tparam tInterfaces    The list of interfaces being implemented.
template<typename ... tInterfaces>
class DECLSPEC_NOVTABLE ComEntryV;


template<typename tInterface, typename ... tTail>
class DECLSPEC_NOVTABLE ComEntryV<tInterface, tTail...> :
	public ComEntryCompound
					<
						ComEntry<tInterface>,
						ComEntryV<tTail...>
					>
{ };

template <typename tInterface>
class DECLSPEC_NOVTABLE ComEntryV<tInterface> :
	public ComEntry1<tInterface>
{ };

#endif	//(SUPPORTED_CPP0X != 0)


////////////////////////////////////////////////////////////////////////////////

//! \brief
//! Implements exactly one COM interface and provides discovery information
//! about this interface, plus an additional interface which the implemented
//! interface derives from.
//! 
//! \tparam tInterface  The interface being implemented.
//! 
//! \tparam tParent    The parent class that \a tInterface derives from.
template<typename tInterface, typename tParent>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscovery1 :
	public tInterface,
	public ComEntryCompound
		<
			ComEntryDiscoverable<tInterface>,
			ComEntry2StepDiscoverable<tParent, tInterface>
		>
{
public:
	typedef tInterface Interface;
};


//! \brief
//! Implements exactly one COM interface and provides discovery information
//! about this interface, plus two additional interfaces which the implemented
//! interface derives from.
//! 
//! \tparam tInterface  The interface being implemented.
//! 
//! \tparam tParent1    The first parent class that \a tInterface derives from.
//! 
//! \tparam tParent2    The second parent class that \a tInterface derives from.
template<typename tInterface, typename tParent1, typename tParent2>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscovery2 :
	public ComEntryCompound
		<
			ComEntry2StepDiscoverable<tParent1, tInterface>,
			ComEntryWithParentDiscovery1<tInterface, tParent2>
		>
{
public:
	typedef tInterface Interface;
};


//! \brief
//! Implements exactly one COM interface and provides discovery information
//! about this interface, plus three additional interfaces which the implemented
//! interface derives from.
//! 
//! \tparam tInterface  The interface being implemented.
//! 
//! \tparam tParent1    The first parent class that \a tInterface derives from.
//! 
//! \tparam tParent2    The second parent class that \a tInterface derives from.
//! 
//! \tparam tParent3    The third parent class that \a tInterface derives from.
template<typename tInterface, typename tParent1, typename tParent2, typename tParent3>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscovery3 :
	public ComEntryCompound
		<
			ComEntry2StepDiscoverable<tParent1, tInterface>,
			ComEntryWithParentDiscovery2<tInterface, tParent2, tParent3>
		>
{
public:
	typedef tInterface Interface;
};


//! \brief
//! Implements exactly one COM interface and provides discovery information
//! about this interface, plus four additional interfaces which the implemented
//! interface derives from.
//! 
//! \tparam tInterface  The interface being implemented.
//! 
//! \tparam tParent1    The first parent class that \a tInterface derives from.
//! 
//! \tparam tParent2    The second parent class that \a tInterface derives from.
//! 
//! \tparam tParent3    The third parent class that \a tInterface derives from.
//! 
//! \tparam tParent4    The fourth parent class that \a tInterface derives from.
template<typename tInterface, typename tParent1, typename tParent2, typename tParent3, typename tParent4>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscovery4 :
	public ComEntryCompound
		<
			ComEntry2StepDiscoverable<tParent1, tInterface>,
			ComEntryWithParentDiscovery3<tInterface, tParent2, tParent3, tParent4>
		>
{
public:
	typedef tInterface Interface;
};


//! \brief
//! Implements exactly one COM interface and provides discovery information
//! about this interface, plus file additional interfaces which the implemented
//! interface derives from.
//! 
//! \tparam tInterface  The interface being implemented.
//! 
//! \tparam tParent1    The first parent class that \a tInterface derives from.
//! 
//! \tparam tParent2    The second parent class that \a tInterface derives from.
//! 
//! \tparam tParent3    The third parent class that \a tInterface derives from.
//! 
//! \tparam tParent4    The fourth parent class that \a tInterface derives from.
//! 
//! \tparam tParent5    The fifth parent class that \a tInterface derives from.
template<typename tInterface, typename tParent1, typename tParent2, typename tParent3, typename tParent4, typename tParent5>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscovery5 :
	public ComEntryCompound
		<
			ComEntry2StepDiscoverable<tParent1, tInterface>,
			ComEntryWithParentDiscovery4<tInterface, tParent2, tParent3, tParent4, tParent5>
		>
{
public:
	typedef tInterface Interface;
};

////////////////////////////////////////////////////////////////////////////////

// Variadic-template versions


#if (SUPPORTED_CPP0X != 0)


//! \brief
//! Implements exactly one COM interface and provides discovery information
//! about this interface, plus an arbitrary number of interfaces which the
//! implemented interface derives from.
//! 
//! \tparam tInterface  The interface being implemented.
//! 
//! \tparam tParents    The variable-length list of parent class that
//!                     \a tInterface derives from.
template <typename tChild, typename ... tParents>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscoveryV;

template <typename tChild, typename tParent, typename ... tTail>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscoveryV<tChild, tParent, tTail...> :
	public ComEntryCompound
		<
			ComEntry2StepDiscoverable<tParent, tChild>,
			ComEntryWithParentDiscoveryV<tChild, tTail...>
		>
{
public:
	typedef tChild Interface;
};

template <typename tChild, typename tParent>
class DECLSPEC_NOVTABLE ComEntryWithParentDiscoveryV<tChild, tParent> :
	public ComEntryWithParentDiscovery1<tChild, tParent>
{ };

#endif 	//(SUPPORTED_CPP0X != 0)



////////////////////////////////////////////////////////////////////////////////

//! \brief
//! Stores the number of references to a COM object instance.
//! 
//! \tparam tThreadSafe If true, increment/decrement operations are implemented
//!                     via interlocking. This allows for class utilization in
//!                     a free threaded environment.
//! 
//! \see ComInstance
template<bool tThreadSafe = false>
struct ComRefCount
{
	LONG myNumRefs;

	//! Creates a default instance of the COM reference counter.
	//! 
	//! \remarks
	//! The number of references is set to one because the entity in which
	//! the object is created implicitly holds one reference to the object.
	ComRefCount() : myNumRefs(1L) { }

	//! Increments the reference counter based on the threading model
	//! specified in the \a tThreadSafe template argument.
	ULONG STDMETHODCALLTYPE AddRef()
	{
		if (!tThreadSafe)
			return ++myNumRefs;
		else
			return InterlockedIncrement(&myNumRefs);
	}


	//! \brief
	//! Decrements the reference counter based on the threading model
	//! specified in the \a tThreadSafe template argument. If the reference
	//! counter becomes non-positive, deletes the object.
	//! 
	//! \tparam tObj   The type of the object that may be deleted.
	//! 
	//! \param theObj  The pointer to the object that may be deleted if the
	//!                reference counter becomes non-positive.
	//! 
	//! Because this method uses templates, the object class does not have to
	//! have a virtual destructor to perform its deletion.
	template<typename tObj>
	ULONG STDMETHODCALLTYPE Release(tObj * theObj)
	{
		ULONG aNumRefs;

		if (!tThreadSafe)
			aNumRefs = --myNumRefs;
		else
			aNumRefs = InterlockedDecrement(&myNumRefs);

		if (aNumRefs <= 0L)
			delete theObj;

		return aNumRefs;
	}
};

////////////////////////////////////////////////////////////////////////////////

//! \brief
//! Represents an instance of a COM object and provides top-level implementation
//! of the IUnknown reference.
//! 
//! \tparam tImpl
//!     The type of the class that provides the actual functionality.
//!     The class is that is must always have the following interface discovery
//!     static member function:
//!     \code
//!       template<typename tObject>
//!       static HRESULT InternalQueryInterface(REFIID, tObject *, void **);
//!     \endcode
//!     Typically tImpl derives from one of the ComEntry* specialization
//!     based on the interfaces that the class implements.
//!     
//! 
//! \tparam tThreadSafe
//!     If true, increment/decrement operations of the COM reference
//!     counter are implemented via interlocking. This allows for class
//!     creating free threaded objects.
template<typename tImpl, bool tThreadSafe = false>
class ComInstance : public tImpl
{
protected:
	ComRefCount<tThreadSafe> myRefs;


public:
	ComInstance()
	{ }

#if (SUPPORTED_CPP0X != 0)

	template<typename ... tParams>
	ComInstance(tParams && ... theParams)
		: tImpl( std::forward<tParams>(theParams)... )
	{ }
	
#else
	
	template<typename tParam1>
	ComInstance(tParam1 theParam1)
		: tImpl(theParam1)
	{ }

	template<typename tParam1, typename tParam2>
	ComInstance(tParam1 theParam1, tParam2 theParam2)
		: tImpl(theParam1, theParam2)
	{ }

	template<typename tParam1, typename tParam2, typename tParam3>
	ComInstance(tParam1 theParam1, tParam2 theParam2, tParam3 theParam3)
		: tImpl(theParam1, theParam2, theParam3)
	{ }

	template<typename tParam1, typename tParam2, typename tParam3, typename tParam4>
	ComInstance(tParam1 theParam1, tParam2 theParam2, tParam3 theParam3, tParam4 theParam4)
		: tImpl(theParam1, theParam2, theParam3, theParam4)
	{ }

	template<typename tParam1, typename tParam2, typename tParam3, typename tParam4, typename tParam5>
	ComInstance(tParam1 theParam1, tParam2 theParam2, tParam3 theParam3, tParam4 theParam4, tParam5 theParam5)
		: tImpl(theParam1, theParam2, theParam3, theParam4, theParam5)
	{ }
	
#endif	// (SUPPORTED_CPP0X != 0)


public:
	
	//! \brief    Top-level implementation of IUnknown::AddRef.
	ULONG STDMETHODCALLTYPE AddRef()
	{
		return myRefs.AddRef();
	}

	//! \brief    Top-level implementation of IUnknown::Release.
	ULONG STDMETHODCALLTYPE Release()
	{
		return myRefs.Release(this);
	}

	//! \brief    Top-level implementation of IUnknown::QueryInterface.
	//! 
	//! \remarks
	//! This method implies that the implementation class provides
	//! interface discovery information via the \a InternalQueryInterface
	//! static member function.
	STDMETHODIMP QueryInterface(REFIID theIID, void ** theOut)
	{
		HRESULT aRes = tImpl::InternalQueryInterface(theIID, this, theOut);

		if ( SUCCEEDED(aRes) )
			AddRef();

		return aRes;
	}
};

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Russian Federation Russian Federation
Kirill Osipov constantly lives in Petrozavodsk, Russia. He enjoys listening to music, playing the guitar, learning German, and, occasionally, drinking beer and whisky.

He is extremely passionate about software development and has recently realized he can be most creative in this particular field.

He also maintains his part time business at http://www.ko-sw.com.

Comments and Discussions