Chap.5: TEMPLATE MANAGER


When going through the preceding example, you probably noticed the complex inheritance statements, especially in class Department. Imagine how ugly this can be in a real life application with several dozen classes connected by about the same number of relations.

Fortunately, you, as the user, do not have to worry about these statements; they can be made completely transparent. The reason is that the declaration of the relations already contains all the information:

// ---------------------------------------------------
Aggregate<Department,Department> departments;
Aggregate<Department,Employee> employees;
Aggregate<Department,Project> projects;
// ---------------------------------------------------

If we know that class Aggregate works with classes AggregateParent and AggregateChild (and that information is stored in the library), then we can see that

from line1: Department must inherit AggregateParent<Department,Department>
            Department must inherit AggregateChild<Department,Department>
from line2: Department must inherit AggregateParent<Department,Employee>
            Employee   must inherit AggregateChild<Department,Employee>
from line3: Department must inherit AggregateParent<Department,Project>
            Project    must inherit AggregateChild<Department,Project>

Instead of coding the inheritance statements by hand, we can write a simple template generator which reads the declarations of the relations, and derives the inheritance statements from them. The full source code of the template manager is enclosed in mgr/mgr.cpp, and has only 450 lines of code after stripping off comments and blank lines.

To make the code generation simple, we reserve two keywords: pattern and Pattern. The first keyword will mark all pattern or relation declarations so that the template manager can find them easily.

// ---------------------------------------------------
pattern Aggregate<Department,Department> departments;
pattern Aggregate<Department,Employee> employees;
pattern Aggregate<Department,Project> projects;
// ---------------------------------------------------

The second keyword will be a cover name for the combined inheritance statements required for the given class. The template manager will generate these Pattern(..) expressions in the form of a macro:

    class Department : Pattern(Department) {
      ... // everything as before
    };
    class Employee : Pattern(Employee) {
      ... // everything as before
    };
    class Project : Pattern(Project) {
      ... // everything as before
    };
     

If we have the Template Manager which already generates code, we can improve two more things:

(1) In the example, all the relations were applications of the same template, AGGREGATE. For this reason, we only had to include one file from the library, aggregate.h. In real life situations, there would be a variety of relations used, and when coding everything by hand, you would have to control which library files were included. Since the Template Manager already knows which patterns/relations are going to be used, it can decide which files must be included. The user then includes only a single file, pattern.h, without worrying about which patterns are used.

(2) Instead of using the template form of the iterators, we can use simplified names derived from the instances of the pattern. These names just hide the proper template, and are created in pattern.h. For example, instead of AggregateIterator<Department,Department> it; you can write pattern_iterator_departments it;

With these features, the code for the example will be much simpler:

#include <iostream.h>
#include <string.h>
#define TEMPLATE_MANAGER
#include "pattern.h" // includes all patterns used here
class Department;
class Employee;
class Project;
class Department : Pattern(Department) {
    ... // everything as before
};
class Employee : Pattern(Employee) {
    ... // everything as before
};
class Project :  Pattern(Project) {
    ... // everything as before
};
// these statements are the roadmap to all patterns/relations
// ---------------------------------------------------
pattern Aggregate<Department,Department,0> departments;
pattern Aggregate<Department,Employee,0> employees;
pattern Aggregate<Department,Project,0> projects;
// ---------------------------------------------------
... // The remaining code is identical, except for the iterators
    // that either have to have the last parameter 0,
    // or must use the simplified form of iterators:

For example:

    AggregateIterator<Department,Department,0> deptIt;
    AggregateIterator<Department,Employee,0>   emplIt;
    AggregateIterator<Department,Project,0>    projIt;

or

    pattern_iterator_departments deptIt;
    pattern_iterator_employees   emplIt;
    pattern_iterator_projects    projIt;

Prior to compiling your program, you run the template manager with your program (or its header file) as the input:

    mgr\mgr mycode.c

You don't have to go through this code generation again and again while debugging the program. You must generate a new pattern.h only when you add or remote a pattern, or when one of the patterns changes. You must link to the Pattern Template library, for example for the Borland compiler:

    bcc -ml -I..\lib test.cpp

RULES FOR USING THE Pattern(..) STATEMENTS:

(a) For every pattern, you must have the full pattern statement.

(b) Every class listed in any of the 'pattern' statements must have a Pattern statement.

(c) This statement must be inserted just before '{' which starts the class description. '{' must be on the same line, and nothing except possibly a comment may follow. Examples:

      class A : Pattern(A) {                     // OK
      class B : Pattern(B) {};                   // not acceptable
      class C : Pattern(C)
      {                                          // not acceptable
      class D : public A, public X, Pattern(D) { // OK

(d) There is one exception to this mechanical use of the statement 'Pattern'. Character ':' must not be used when the class does not inherit from any other application class and, at the same time, it participates passively as the second parameter in one or more statements pattern Array<H,T,i> ... or pattern PtrArray<H,T,i>. Examples:

        class A : Pattern(A) {
            ...
        };
        class B Pattern(B) {  // Note that ':' is missing
            ...
        };
        pattern Array<A,B,0> myArray;

but if B is active in other patterns, the character ':' is needed:

        class A : Pattern(A) {
            ...
        };
        class B : Pattern(B) {  // Note that ':' is used
            ...
        };
        pattern Array<A,B,0> myArr;
        pattern Collection<A,B,0> myCol;

We do not like exceptions, but we could not figure out a programming trick which would make the use of Pattern() more uniform. The good thing is that even if you make a mistake in these rules, the compiler will tell you that you have an error on a particular line. Watch that you have always just one '{' after the Pattern() statement, and nothing more on the same line. If you violate this rule, the compiler message is usually obscure, and relates to the nesting of braces {}.