A Variation on the Curiously Recurring Template Pattern

C/C++ Users Journal June, 2005

Delegating to a derived class

By Arne Adams

Arne Adams is a professional programmer who works with C++, Java, and Smalltalk. He can be contacted at inbox@arneadams.com.

The curiously recurring template pattern [1] splits generic and concrete functionality by delegating to its derived class. In a nutshell, it goes like this:

template <class AnyDerivedClass> 
class CuriouslyRecurringTemplatePattern
{
  protected:
     AnyDerivedClass* This()
     {
       return static_cast
             <AnyDerivedClass*>(this);
     }
};
class ConcreteDerived : 
  public CuriouslyRecurringTemplatePattern
             <ConcreteDerived>
{
};

Figure 1 depicts this code fragment as UML.

For the variation I present here, the term "parametric base class pattern" has been coined [2]. Common to all uses of the pattern that I'm aware of is that a template parameter used to connect generic and concrete functionality is required to fulfill a specific concept. On the other hand, in this article, I do not make any assumptions on the template parameter, and thus, gain flexibility.

A Problem

Assume you have a lot of batch jobs to implement, all of which follow a specific scheme. Moreover, assume that this definition is used to export or import data between different persistent storage systems:

{
    initializeProcess();
    Record record;
    try
    {
        while(getRecord(record))
        {
            processRecord(record);
        }
    }
    catch(std::exception& oooops)
    {
        handle(oooops);
    }
}

Because of this, you decide to make your batch jobs a template:

template<class Record > class BatchProcess
{
public:
    void operator()();
protected:
    void initializeProcess();
    bool getRecord(T&);
    void processRecord(T&);
    void handle(std::exception&);
};

In the case where you import file-persistent records into a database, the general initializeProcess function could use trait classes to find the file and table(s) for the record, open the file, and connect to the database. getRecord would just read one line of the file, and processRecord would insert the record into the corresponding table.

template<class Record> void 
BatchProcess<Record>::operator()()
{
    initializeProcess();
    Record record;
    try
    {
        while(getRecord(record))
        {
          processRecord(record);
        }
    }
    catch(std::exception& oooops)
    {
        handle(oooops);
    }
}

For a while, all your generic implementations work fine, and defining a BatchProcess for a new Record consists of a typedef and some trait definitions. But later, you find that you need to check some records. Hence, you add member functions—which default to empty functions—to do exactly this:

template<class Record > class BatchProcess
{
 ...
protected:
    ...
    bool check(const Record&)const;
    void checkFailed(const Record&);
};
template<class Record> void 
BatchProcess<Record>::operator()()
{
    initializeProcess();
    Record record;
    try
    {
        while(getRecord(record))
        {
            if(check(record))
                processRecord(record);
            else
                checkFailed(record);
        }
    }
    catch(std::exception& oooops)
    {
        handle(oooops);
    }
}

Those BatchProcesses that need to do something could specialize the check functions and all would be fine. On the other hand, you could get rid of the if(true) for checks that are empty functions by moving the check to a template class:

template<class Record> struct NeedsCheck
{
    static const bool value = false;
};
template<class Record, bool needsCheck = NeedsCheck<Record>::value > 
struct CheckerIfNeeded
{
    static void checkedProcess(Record&, BatchProcess<Record>&);
};
template<class Record, bool needsCheck> void 
CheckerIfNeeded<Record,needsCheck>::checkedProcess(Record& record, 
                                BatchProcess<Record>& process)
{
    process.processRecord(record); // per default no check
}
template<class Record> 
struct CheckerIfNeeded<Record,true>
{
    static void checkedProcess(Record&, BatchProcess<Record>&);
};

template<class Record> void 
CheckerIfNeeded<Record,true>::checkedProcess(Record& record, 
                                BatchProcess<Record>& process)
{
    if(process.check(record))
        process.processRecord(record);
    else
        process.checkFailed(record);
}

And the process function would now look like this:

template<class Record, class AnyThing> void 
BatchProcess<Record,AnyThing>::operator()()
{
    initializeProcess();
    Record record;
    try
    {
        while(getRecord(record))
        {
            CheckerIfNeeded<Record>::checkedProcess(record,*this);
        }
    }
    catch(std::exception& oooops)
    {
        handle(oooops);
    }
}

All this works fine until some BatchProcesses need a state that others don't need; for instance, a specific record could need a lookup table inside the check function, which is costly to calculate and is constant during the processing of records.

Of course, you could fully specialize the BatchProcess template for these records. However, this would mean coding everything in these specializations manually. Getting rid of copy-and-paste programming was your intention when you started the template design.

Luckily, a variation of the curious recurring template pattern comes to the rescue and goes like this:

struct Nothing{};
template<class Record, class AnyThing = 
           Nothing> class BatchProcess : public AnyThing
{
	... rest as before
};

You simply cater to any future needs by supplying the possibility to add AnyThing to your special BatchProcess.

Your special lookup table could be plugged-in like this:

struct RecWithShrek
{
   // your Recorddefinition goes here
};
struct ExpensiveLookupTable
{
   std::map<long,int> veryExpensiveToFill_;
};
template<> void 
BatchProcess<RecWithShrek,ExpensiveLookupTable>::initializeProcess()
{
    veryExpensiveToFill_[1]=2;  
    // OK - if this one is really too expensive then you might   
    // have serious problems :-)
}
template<> bool 
BatchProcess<RecWithShrek,ExpensiveLookupTable>::
                    check(const RecWithShrek&)const
{
                   // use your expensive lookup table
    return true;    // and do not ignore the results :-)
}

Use of this code would lead to:

BatchProcess<RecWithShrek,ExpensiveLookupTable> processShrek;
processShrek();

In general, extra care has to be taken when passing parameters between member functions through the use of member variables. Often, a function signature is the best way to document and enforce the values that a function needs to work correctly.

References

  1. [1] Coplien, James. "Curiously Recurring Template Pattern," C++ Report, February 1995.
  2. [2] http://www.boost.org/libs/spirit/phoenix/doc/actors_revisited.html.