Question

I discovered template metaprogramming more than 5 years ago and got a huge kick out of reading Modern C++ Design but I never found an opertunity to use it in real life.

Have you ever used this technique in real code?

Contributors to Boost need not apply ;o)

Was it helpful?

Solution

I once used template metaprogramming in C++ to implement a technique called "symbolic perturbation" for dealing with degenerate input in geometric algorithms. By representing arithmetic expressions as nested templates (i.e. basically by writing out the parse trees by hand) I was able to hand off all the expression analysis to the template processor.

Doing this kind of thing with templates is more efficient than, say, writing expression trees using objects and doing the analysis at runtime. It's faster because the modified (perturbed) expression tree is then available to the optimizer at the same level as the rest of your code, so you get the full benefits of optimization, both within your expressions but also (where possible) between your expressions and the surrounding code.

Of course you could accomplish the same thing by implementing a small DSL (domain specific language) for your expressions and the pasting the translated C++ code into your regular program. That would get you all the same optimization benefits and also be more legible -- but the tradeoff is that you have to maintain a parser.

OTHER TIPS

I've found policies, described in Modern C++ Design, really useful in two situations:

  1. When I'm developing a component that I expect will be reused, but in a slightly different way. Alexandrescu's suggestion of using a policy to reflect a design fits in really well here - it helps me get past questions like, "I could do this with a background thread, but what if someone later on wants to do it in time slices?" Ok fine, I just write my class to accept a ConcurrencyPolicy and implement the one I need at the moment. Then at least I know the person who comes behind me can write and plug in a new policy when they need it, without having to totally rework my design. Caveat: I have to reign myself in sometimes or this can get out of control -- remember the YAGNI principle!

  2. When I'm trying to refactor several similar blocks of code into one. Usually the code will be copy-pasted and modified slightly because it would have had too much if/else logic otherwise, or because the types involved were too different. I've found that policies often allow for a clean one-fits-all version where traditional logic or multiple inheritance would not.

I've used it in the inner loops of a game's graphics code, where you want some level of abstraction and modularity but can't pay the cost of branches or virtual calls. Overall it was a better solution than a proliferation of handwritten special-case functions.

Template metaprogramming and expression templates are becoming more popular in the scientific community as optimization methods that offload some of the computational effort onto the compiler while maintaining some abstraction. The resulting code is larger and less readable, but I have used these techniques to speed up linear algebra libraries and quadrature methods in FEM libraries.

For application-specific reading, Todd Veldhuizen is a big name in this area. A popular book is C++ and Object Oriented Numeric Computing for Scientists and Engineers by Daoqi Yang.

Template meta programming is a wonderful and power technique when writing c++ libraries. I've used it a few time in custom solutions, but usually a less elegant old style c++ solution is easier to get through code review and easier to maintain for other users.

However, I've got a lot of mileage out of template meta programming when writing reusable components/libraries. I'm not talking anything as large some of Boost's stuff just smallish components that will be reused frequently.

I used TMP for a singleton system where the user could specify what type of singleton they desired. The interface was very basic. Underneath it was powered by heavy TMP.

template< typename T >
T& singleton();

template< typename T >
T& zombie_singleton();

template< typename T >
T& phoenix_singleton();

Another successful use was simplifying our IPC layer. It is built using classic OO style. Each message needs to derive from an abstract base class and override some serialization methods. Nothing too extreme, but it generates a lot of boiler plate code.

We threw some TMP at it and automated the generation of all the code for the simple case of messages containing only POD data. The TMP messages still used the OO backend but they massively reduce the amount of boiler plate code. The TMP was also used to generate the message vistor. Over time all our message migrated to the TMP method. It was easier and less code to build a simple POD struct just for message passing and add the few (maybe 3) lines needed to get the TMP to generate the classes than it was to derive a new message to send a regular class across the IPC framework.

I use template metaprogramming all the time, but in D, not C++. C++'s template metalanguage was originally designed for simple type parametrization and became a Turing complete metalanguage almost by accident. It is therefore a Turing tarpit that only Andrei Alexandrescu, not mere mortals, can use.

D's template sublanguage, on the other hand, was actually designed for metaprogramming beyond simple type parameterization. Andrei Alexandrescu seems to love it, but other people can actually understand his D templates. It's also powerful enough that someone wrote a compile-time raytracer in it as a proof of concept.

I guess the most useful/non-trivial metaprogram I ever wrote in D was a function template that, given a struct type as the template parameter and a list of column header names in an order corresponding to the variable declarations in the struct as a runtime parameter, will read in a CSV file, and return an array of structs, one for each row, with each struct field corresponding to a column. All type conversions (string to float, int, etc.) are done automatically, based on the types of the template fields.

Another good one, which mostly works, but still doesn't handle a few cases properly, is a deep copy function template that handles structs, classes, and arrays properly. It uses only compile time reflection/introspection, so that it can work with structs, which, unlike full-blown classes, have no runtime reflection/introspection capabilities in D because they're supposed to be lightweight.

Most programmers who use template metaprogramming use it indirectly, through libraries like boost. They don't even probably know what is happening behind the scenes, only that it makes the syntax of certain operations much much easier.

I've used it quite a bit with DSP code, especially FFTs, fixed size circular buffers, hadamard transforms and the like.

For those familiar with Oracle Template Library (OTL), boost::any and Loki library (the one described in Modern C++ Design) here's the proof of concept TMP code that enables you to store one row of otl_stream in vector<boost::any> container and access data by column number. And 'Yes', I'm going to incorporate it in production code.

#include <iostream>
#include <vector>
#include <string>
#include <Loki/Typelist.h>
#include <Loki/TypeTraits.h>
#include <Loki/TypeManip.h>
#include <boost/any.hpp>
#define OTL_ORA10G_R2
#define OTL_ORA_UTF8
#include <otlv4.h>

using namespace Loki;

/* Auxiliary structs */
template <int T1, int T2>
struct IsIntTemplateEqualsTo{
    static const int value = ( T1 == T2 );
};

template <int T1>
struct ZeroIntTemplateWorkaround{
    static const int value = ( 0 == T1? 1 : T1 );
};


/* Wrapper class for data row */
template <class TList>
class T_DataRow;


template <>
class T_DataRow<NullType>{
protected:
    std::vector<boost::any> _data;
public:
    void Populate( otl_stream& ){};
};


/* Note the inheritance trick that enables to traverse Typelist */
template <class T, class U>
class T_DataRow< Typelist<T, U> >:public T_DataRow<U>{
public:
    void Populate( otl_stream& aInputStream ){
        T value;
        aInputStream >> value;
        boost::any anyValue = value;
        _data.push_back( anyValue );

        T_DataRow<U>::Populate( aInputStream );
    }

    template <int TIdx>
    /* return type */
    Select<
        IsIntTemplateEqualsTo<TIdx, 0>::value,
        typename T,
        typename TL::TypeAt<
            U,
            ZeroIntTemplateWorkaround<TIdx>::value - 1
        >::Result
    >::Result
    /* sig */
    GetValue(){
    /* body */
        return boost::any_cast<
            Select<
                IsIntTemplateEqualsTo<TIdx, 0>::value,
                typename T,
                typename TL::TypeAt<
                    U,
                    ZeroIntTemplateWorkaround<TIdx>::value - 1
                >::Result
            >::Result
        >( _data[ TIdx ] );
    }
};


int main(int argc, char* argv[])
{
    db.rlogon( "AMONRAWMS/WMS@amohpadb.world" ); // connect to Oracle
    std::cout<<"Connected to oracle DB"<<std::endl;
    otl_stream o( 1, "select * from blockstatuslist", db );

    T_DataRow< TYPELIST_3( int, int, std::string )> c;
    c.Populate( o );
    typedef enum{ rcnum, id, name } e_fields; 
    /* After declaring enum you can actually acess columns by name */
    std::cout << c.GetValue<rcnum>() << std::endl;
    std::cout << c.GetValue<id>() << std::endl;
    std::cout << c.GetValue<name>() << std::endl;
    return 0;
};

For those not familiar with mentioned libraries.

The problem with OTL's otl_stream container is that one can access columns data only in sequential order by declaring variables of appropriate type and applying the operator >> to otl_stream object in the following way:

otl_stream o( 1, "select * from blockstatuslist", db );
int rcnum; 
int id;
std::string name;
o >> rcnum >> id >> name; 

It's not always convenient. The workaround is to write some wrapper class and to populate it with data from otl_stream. The desire is to be able to declare the list of column types and then:

  • take the type T of the column
  • declare variable of that type
  • apply olt_stream::operator >>(T&)
  • store the result (in the vector of boost::any)
  • take the type of the next column and repeat until all columns are processed

You can do all this with the help of Loki's Typelist struct, template specialization and inheritance.

With the help of Loki's library constructs you can also generate bunch of GetValue functions that return values of appropriate type deducing it from column's number (actually number of type in Typelist).

No I haven't used it in production code.

Why?

  1. We have to support 6+ platforms with native platform compilers. It's hard enough to use STL in this environment let alone modern template techniques.
  2. Developers don't seem to be keeping up C++ advances anymore. We use C++ when we have to. We have legacy code with legacy designs. New code is done in something else e.g., Java, Javascript, Flash.

Almost 8 months after asking this I've finally used some TMP, I use a TypeList of interfaces in order to implement QueryInterface in a base class.

I use it with boost::statechart for large statemachines.

Yes I have, mostly to do some things that resemble duck-typing when I was wrapping a legacy API in a more modern C++ interface.

Don't do that. The reason behind that is as follows: by nature of template metaprogramming, if some part of your logic is done at compile-time, every logic that it is dependent on must be done at compile time as well. Once you start it, do one portion of your logic at compile time, there is no return. The snowball will keep on rolling and there is no way to stop it.

For example, you can't iterate on the elements of a boost::tuple<>, because you can only access them at compile time. You must use template metaprogramming to achieve what would have been easy and straightforward C++, and this always happens when the users of C++ aren't careful enough not to move too many things to compile-time. Sometimes it is difficult to see when a certain use of compiletime logic would become problematic, and sometimes programmers are eager to try and test what they've read in Alexandrescu's. In any case, this is a very bad idea in my opinion.

Many programmers don't use templates much because of the poor compiler support up until recently. However, while templates have had a lot of issues in the pas, newer compilers have much better support. I write code that has to work with GCC on Mac and Linux as well as Microsoft Visual C++ and it's only with GCC 4 and VC++ 2005 that these compiler have supported the standard really well.

Generic programming via templates is not something you need all the time but is definitely a useful code to have in your toolbox.

The obvious example container classes but templates are also useful for many other things. Two examples from my own work are:

  • Smart pointers ( e.g. Reference-counted, copy-on-write, etc.)
  • Math support classes such as Matrices, vectors, splines, etc. that need to support a variety of data types and still be efficient.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top