Creating a Practical C++ Template | CodeGuru

Creating a Practical C++ Template

Environment: VC6, VC.Net, Win32 Introduction Most C++ books and articles love to use containers or mathematical functions (like compile time recursion) to represent the power of the template. Yeah, it is cool, but it seems only a scientific programmer can benefit from meta programming. In this article, I’ll show how an application developer can enjoy […]

Written By
CodeGuru Staff
CodeGuru Staff
Aug 30, 2002
3 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

Environment: VC6, VC.Net, Win32

Introduction

Most C++ books and articles love to use containers or mathematical functions (like compile time recursion) to represent the power of the template. Yeah, it is cool, but it seems only a scientific programmer can benefit from meta programming. In this article, I’ll show how an application developer can enjoy the fun of programming templates, too.

  1. Remove code duplication.

    Imagine I build a MFC dialog box that has two different kinds of button, a CBitmapButton and normal CButton; I use two CArray instances, respectively, to hold them. Therefore, in the .h file, I’ll have the following declaration:

    #include<afxtempl.h>
    class MyDialog
    {
    //….
    private:
      CArray<CButton,CButton&> m_buttonArray;
      CArray<CBitmapButton,CBitmapButton&> m_bmpButtonArray;
    };
    Now, I need a function to hide all the buttons on the dialog box. The first iteration needed to write the function is:
    
    void MyDialog::ShowAllButtons(BOOL bShow)
    {
      int nIndex=0;
      for (nIndex=0;nIndex < m_buttonArray.GetSize();++nIndex)
    {
        m_buttonArray[nIndex].ShowWindow(bShow);
    }
        for (nIndex=0;nIndex<m_bmpButtonArray.GetSize();++nIndex)
    {
        m_bmpButtonArray[nIndex].ShowWindow(bShow);
    }
    }
    
    It seems what I need is to copy and paste and change the variable names, but it smells bad for a real programmer who thinks programming is more than a job but also is an art. You can imagine that, if there are 10 different types of button arrays, I have to use Ctrl+C and Ctrl+V 10 more times; it sucks. There must be something that can be done, and now the template kicks in.
    Let’s add one more template function to the MyDialog; let’s call it ShowButtons. Now, the .h file becomes:
    class MyDialog
    {
    //....
    private:
      void ShowAllButtons(BOOL bShow);
      //new template
    function
      template <typename ButtonType>
    
    void ShowButtons(CArray<ButtonType,ButtonType&>
                     &rButtonArray,BOOL bShow);
    private:
    CArray<CButton,CButton&> m_buttonArray;
    CArray<CBitmapButton,CBitmapButton&> m_bmpButtonArray;
    };
    
    We’ll define it as follows (there is still no export keyboard supported in VC++ yet, so I defined the function in the same .h file).
    
    template <typename ButtonType>
    void
    CMyDialog::ShowButtons(CArray<ButtonType,ButtonType&>
                           &rButtonArray,BOOL bShow)
    {
      for (int nIndex=0;nIndex<rButtonArray.GetSize();++nIndex)
    {
    rButtonArray[nIndex].ShowWindow(bShow);
    }
    }
    
    and rewrite the ShowAllButtons as follows:
    
    void
    CMyDialog::ShowAllButtons(BOOL bShow)
    {
      ShowButtons(m_buttonArray,bShow);
      ShowButtons(m_bmpButtonArray,bShow);
    }
    
    the compiler will deduce the type automatically. You are also welcome to call the ShowButtons with an explicit type specified, as in:
    
    ShowButtons<CButton>(m_buttonArray,bShow);
    ShowButtons<CBitmapButton>(m_bmpButtonArray,bShow);
    
    These both work, and now, there is no need to copy and paste anymore because the code is generated by the compiler through the template. This is the power of C++ versus VB or any other language.
  2. Generic callback:

    When programming Windows with C++, there is a chance that you will have to supply a C-style callback function to a Win32 API (like CreateThread, SetTimer, and so forth). If the callback function required has a LPVOID as an argument, everything is okay. Using the old trick passes this pointer as an LPVOID argument. But, unfortunately, there is an API called SetWinEventHook; its prototype is:

    HWINEVENTHOOK WINAPI SetWinEventHook(
    UINT eventMin,
                UINT
    eventMax,
    HMODULE hmodWinEventProc,
                WINEVENTPROC
    lpfnWinEventProc,
    DWORD idProcess,
                DWORD
    idThread,
    UINT dwflags)
    ;
    which takes a callback function, WINEVENTPROC. Its function signature is:
    VOID CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook,
    
    DWORD event,
    
    HWND hwnd,
                LONG
    idObject,
    
    LONG idChild,
    
    DWORD dwEventThread,
    
    DWORD dwmsEventTime);
    
    Obviously, there is no LPVOID parameter; hence, there is no way to pass this pointer. It is virtually impossible to access the non-static data member inside the WinEventProc function. That is horrible, and a nightmare for an OO developer.
    To solve this problem, just recall an old golden software development rule, “Many design problems can be solved by one more indirection.” So, I created a template class named WinEvent. It has the following structure:
    template <typename WinEventHandler>
    class WinEvent
    {
      public:
      typedef VOID (CALLBACK WinEventHandler::*PfnCallback)
      (HWINEVENTHOOK,DWORD,HWND,LONG,LONG,DWORD,DWORD);
    
      static HWINEVENTHOOK InstallWinEventHook(
                           WinEventHandler *pHandler,
                           PfnCallback pfn,UINT eventMin,UINT
    eventMax,
                           HMODULE ModWinEventProc,DWORD
    idProcess
                           DWORD dThread,UINT dwFlags);
      private:
    
      static WinEventHandler *s_pHandler;
      static PfnCallback s_pfnCallback;
    
      private:
    
      WinEvent()
      ~WinEvent();
    
      static VOID CALLBACK WinEventProc(
                           HWINEVENTHOOK hWinEventHook,
                           DWORD event,HWND hwnd,LONG idObject,
                           LONG idChild,DWORD dwEventThread,
                           DWORD dwmsEventTime);
    };
    
    
    template <typename WinEventHandler>
    HWINEVENTHOOK
    WinEvent<WinEventHandler>::InstallWinEventHook(
          WinEventHandler *pHandler,
          PfnCallback pfn
          UINT eventMin,UINT eventMax,HMODULE hModWinEventProc,
          DWORD idProcess,DWORD idThread,UINT dwFlags)
    {
      s_pHandler=pHandler;
      s_pfnCallback=pfn;
      return SetWinEventHook(eventMin,eventMax,
                             hModeWinEventProc,WinEventProc,
                             idProcess,idThread,dwFlags);
    }
    
    
    
    
    template <typename WinEventHandler>
    VOID CALLBACK WinEvent<WinEventHandler>::WinEventProc(
      HWINEVENTHOOK hWinEventHook,
      DWORD event,
      HWND hwnd,
      LONG idObject,
      LONG idChild,
      DWORD dwEventThread,
      DWORD dwmsEventTime)
    {
      //delegate to the WinEventHandler class's function
      (s_pHandler->*s_pfnCallback)(hWinEventHook,event,hwnd,
                      idObject,aidChild,dwEventThread,
                      dwmsEventTime);
    }
    
    As mentioned above, SetWinEventHook takes only a C-style callback, so I made WinEventProc a static function, to access s_pHandler and s_pfnCallback inside the WinEventProc. I had no choice but made them static too, and now, if my MyDialog class wants to receive the callback, I need to add a member callback function. The new .h declaration becomes:
    class MyDialog
    {
    //same as the above
    private:
    VOID CALLBACK WinEventProc(
                   HWINEVENTHOOK hWinEventHook,DWORD event,HWND
                   hwnd,LONG idObject,LONG idChild,DWORD
                   dwEventThread,DWORD dwmsEventTime);
    };
    
    The function name doesn’t need to be WinEventProc; it can be anything you like, as long as the function signature is correct. The following statement will call the InstallWinEventHook:
    WinEvent<CMyDialog>::InstallWinEventProc
                         (this,WinEventProc,,,,,);
    
    

Conclusion

Technically, the WinEvent class doesn’t have to be a template. It can hold a “hardcoded” EventHandler type, but to reduce the de-coupling, a template is the only choice. I know the WinEvent class is unfinished and there is plenty of space to improve but the above design just shows the practical use of a template and I believe meta-programming has a growing impact in application development setting, too. Please enjoy.

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.