Introduction
If you have ever coded using the Microsoft Assembler, I bet you wish you could have an equivalent to those powerful MASM preprocessor macros. Even if you don't know what I am talking about, there is a trick to make the VC++ compiler do all of your code grinding for you, using C++ templates.
Templates: Type-safe Macros
Basic Use
Templates are like macros, in that they generate code, but in a typesafe way. For example, instead of writing a generic List class inside of a huge, un-maintainable macro with all those line break characters, you can write a template to describe the generic list class:
template <typename Type>
class List
{
void PushFront(Type & newItem); void PopFront();
};
As we all (hopefully) know, template parameters are not restricted to typenames and classes; a constant can be a template parameter as well:
template <const unsigned InvalidValue = -1>
class Handle
{
};
Specialization
In addition to declaring a generic class or function, specialized verions of templates can be written so that the class can still be used by the user, but in a modified form:
class List<IUnknown*>
{
void PushFront(IUnknown* newItem); void PopFront();
};
Externally Using a Template Type or Constant
The keyword typedef can be used to access the type of a template from the outside of a template class definition, and enum can be used to access a constant template parameter:
template <typename Type>
class List
{
typedef Type _ListType;
};
template <const unsigned InvalidValue = -1>
class Handle
{
enum {_InvalidValue = InvalidValue};
};
The Trick
The Gist of It
Using templates, you can write classes simply to refer constants and typenames between each other like so:
<PRE lang=c++>template <const unsigned Constant>
class Const
{
private:
typedef void * _TypeName;
};
class Const<1>
{
public:
typedef int _TypeName;
};template <Typename Type>
class TypeName
{
private:
enum {_Const = 0};
};
class TypeName<int>
{
public:
enum {_Const = 1};
};
Very interesting code. But what does it do? Class Const is a template for an association of a type with a constant, and class TypeName is a template for an association of a constant with a type. So declaring Const<1>::_TypeName var; is equivalent to int var;, and int x = TypeName<int>::_Const; is equivalent to int x = 1;. The members of the general class declaration are private because they are "undefined" and should not be used (void* is used as the generic type, and 0 is the generic constant).
The Trick in Practice
Below is an extended application of the previous concept. It is a quick and incomplete implementation of a template function that converts a VARIANT to another type and returns a reference to the value inside of the VARIANT. Notice how each declaration of XCppType define seperate implemetations of FromVARIANT(...), which is used in ChangeType(...).
template <const VARENUM VarEnum>
class XComType
{
private:
typedef void* CppType;
};
class XComType<VT_INT>
{
public:
typedef int CppType;
};
class XComType<VT_BSTR>
{
public:
typedef BSTR CppType;
};
template <typename Type>
class XCppType
{
private:
enum EnumVarEnum {TypeVarEnum = 0};
inline static void*& FromVARIANT(VARIANT * pVar)
{
return pVar->byref;
}
};
class XCppType<int>
{
public:
enum EnumVarEnum {TypeVarEnum = VT_INT};
inline static int& FromVARIANT(VARIANT * pVar)
{
return pVar->intVal;
}
};
class XCppType<BSTR>
{
public:
enum EnumVarEnum {TypeVarEnum = VT_BSTR};
inline static BSTR& FromVARIANT(VARIANT * pVar)
{
return pVar->bstrVal;
}
};
template <const VARENUM vt>
XComType<vt>::CppType & ChangeType(VARIANT * pVar)
{
VariantChangeType(pVar, pVar, 0, vt);
return XCppType<XComType<vt>::CppType>::FromVARIANT(pVar);
};
int main(int argc, _TCHAR* argv[])
{
VARIANT var;
VariantInit(&var);
var.vt = VT_BSTR;
var.bstrVal = SysAllocString(L"123");
cout << "Converting to VT_INT..." << endl;
cout << ChangeType<VT_INT>(&var) << endl;
cout << "Converting to VT_BSTR..." << endl;
char bBuffer[32];
wsprintfA(bBuffer, "%ls", ChangeType<VT_BSTR>(&var));
cout << bBuffer << endl;
return 0;
}
The concept can easily be enhanced to make easy C++, type-friendly wrapper classes for VARIANT or SAFEARRAY. If you use your imagination (yes, programmers have them), you can grasp the potential for this trick. As a matter of fact, ATL uses this concept for their CComSafeArray. Look at the implementation:
template <
typename T,
VARTYPE _vartype = _ATL_AutomationType< T >::type
>
class CComSafeArray
Look a bit familiar? Find the declaration for _ATL_AutomationType< T >::type and its minions. But I came up with the idea on my own... Scout's Honor :)