MFC TN004--Template Classes

Created: April 15, 1992

ABSTRACT

This technical note describes MFC template class issues and the MFC templdef template expansion sample code.

The Microsoft Foundation Class (MFC) library provides a full-featured set of C++ object classes for the MicrosoftÒ WindowsÔ graphical environment. It includes classes that directly support application development for Windows as well as general-purpose classes for collections, files, persistent storage, exceptions, diagnostics, memory management, strings, and time. Each MFC technical note describes a feature of MFC using code fragments and examples.

THE PROBLEM

The C++ community has long recognized the need for template classes—classes that can be instantiated based on an exact type that the user specifies. A common example is a linked list class—a linked list of what type of object? With template classes the user of the class can specify “I want a linked list of CFoo objects.”

THE TEMPLDEF SAMPLE

Although the C++ community has generally agreed on a syntax for template classes, details have not been worked out. We found it useful to provide template classes to the user in the interim and used a syntax for our template classes based on that generally used within the C++ community. A simple template expander called templdef expanded and slightly modified this syntax. For this expansion, the user specifies a “typedef’ed” name to be used as an alternative to the template_class<type_specifier> class name.

The user can begin to write template classes following the generally agreed upon syntax for C++ templates. This code can be ported to the final agreed upon syntax when the ANSI C++ report is issued. Also, the user can consider the MFC template classes as general examples of how one writes templates. Furthermore, the templdef source code provides an example of how one can use the MFC.

To provide easily readable output from the templdef template expander, we leave comments in the expanded output and textually substitute the type parameters the user specifies. If the user wants to expand template classes over complicated types, we suggest using typedef type names for template parameters.

Users who want to experiment with template classes and templdef should first write and completely debug a nontemplate version of their class. For example, first write a linked-list-of-CFoos class. When that is working, generalize your linked-list-of-CFoos class to a linked-list-of-whatever template class. By taking this approach, you introduce the complicating factor of template <class CXyz> syntax after you have an already working linked-list class. Finally, you should expect some small changes between the template classes you write today and the final syntax agreed upon by the ANSI C++ committee.

A good way to get started is by examining the CTT template classes that MFC provides (found in the MFC\SAMPLE\TEMPLDEF subdirectory). The MKCOLL.BAT file has examples of the command-line syntax of the templdef tool. The general form of the command-line syntax is modeled after a C++ typedef of the equivalent template class. For example:

typedef CArray<CString> CStringArray;

becomes:

templdef "CArray<CString> CStringArray" array.ctt strarray.h

strarray.cpp

The actual MFC template classes are slightly more complicated; they take a number of Boolean constant template parameters that allow several flavors of arrays to be expanded.

TEMPLATE SOURCE FORMATTING

A source file containing a templdef template class is divided into three sections:

The first section consists of comments, usage instructions, copyright notices, and the like. templdef strips this first section, so it is not emitted.

The second section starts with a special directive:

//$DECLARE_TEMPLATE

after which the template class declaration appears. templdef processes this section to result in the output .H file.

After the declaration section comes a special directive:

//$IMPLEMENT_TEMPLATE

followed by the member function definitions that the template class requires. This final section is processed into the resulting CPP output file.

Friend functions, static member definitions, and the like can be included as needed. Except for #if/#else/#endif sections expanded on a template parameter (see next section), preprocessor commands are left unexpanded in the output files. Thus, the native compiler preprocessor can do additional processing.

One restriction of the templdef implementation is that the first occurrence of a template declaration (typically the template class declaration) defines all naming correspondences between template formal parameters and actual parameters throughout the template class file. Thus, template parameter names must be used consistently throughout all template declarations and definitions in the template class file. This approach is consistent with the fact that you are expanding on only one particular, out of n possible, template possibility.

TEMPLATE PARAMETERS AND SPECIAL #IF/#ELSE/#ENDIF PROCESSING

templdef template parameters can be either types or compile-time constant expressions. In particular, if a template parameter is 0 or 1, templdef evaluates (only) #if/#else/#endif statements based on that constant parameter and emits only those lines of code corresponding to the TRUE condition. See MKCOLL.BAT and the MFC template classes for examples of how to use this feature.

The #if/#else/#endif expansion depends on having the manifest constant 1 or 0 as a template parameter. For example, if you say TRUE or FALSE instead, templdef leaves in the #if/#else/#endif expressions for the preprocessing phase of the compiler to expand later. Also, this templdef evaluation does not take nested #if/#else statements into account. Either be sure that your expansion corresponds to an innermost nesting and use 0 or 1 as your Boolean constant template parameter, or say FALSE or TRUE and let the compiler’s preprocessing phase do the #if/#else/#endif expansion later. Another templdef limitation in this area is that it expects #if/#else/#endif statements to be written following the common convention of no intervening space after the # sign. If you need to write # .... if / # .... else / # .... endif, let the compiler’s preprocessor handle the job.