Click here to Skip to main content
Click here to Skip to main content

C++ Templates - Defining Associations Between Types, Classes, and Constants

By , 18 Jul 2003
Rate this:
Please Sign up or sign in to vote.

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);  // Pushes newItem to the front
    void PopFront();                 // Pops Type at front of list 

    // ...
};

As we all (hopefully) know, template parameters are not restricted to typenames and classes; a constant can be a template parameter as well:

// Uses a contant template parameter with a standard value
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:

// Specialization of previously declared class List
class List<IUnknown*>
{
    void PushFront(IUnknown* newItem);  // Calls AddRef() on newItem
    void PopFront();                 // Calls Release() on poped item

    // ...
};

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(...).

// Behold, my perversion of C++
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;
    }
};


// Changes the type of a VARIANT and returns a
// reference to the converted value inside
template <const VARENUM vt>
XComType<vt>::CppType & ChangeType(VARIANT * pVar)
{
    VariantChangeType(pVar, pVar, 0, vt);
    return XCppType<XComType<vt>::CppType>::FromVARIANT(pVar);
};
// ... For a real example, there would be implementations for all the 
// ... VARENUM's and associated C++ types
//

// The Driver program:

int main(int argc, _TCHAR* argv[])
{
    // Simple test
    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];
    // Simplest way to convert WCHAR to char that I know

    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:  

// Source: ATL Reference Library
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 Smile | :)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Cap'n Code

United States United States
No Biography provided

Comments and Discussions

 
GeneralSyntax error PinmemberNemanja Trifunovic19-Jul-03 6:51 
GeneralRe: Syntax error PinmemberCap'n Code19-Jul-03 15:51 
GeneralRe: Syntax error PinmemberNemanja Trifunovic19-Jul-03 18:28 
GeneralVC6 bugs PinmemberJohn M. Drescher19-Jul-03 5:55 
GeneralRe: VC6 bugs PinmemberCap'n Code19-Jul-03 15:57 
GeneralRe: VC6 bugs PinmemberJohn M. Drescher19-Jul-03 18:47 
I saw that you were using VC.NET in the supported platforms but I thought I would save a few people problems when they try to compile your examples on VC5 or VC6. Smile | :)
 
John
GeneralCaptain PinsitebuilderUwe Keim19-Jul-03 0:29 
GeneralRe: Captain PinmemberCap'n Code19-Jul-03 16:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 19 Jul 2003
Article Copyright 2003 by Cap'n Code
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid