views:

741

answers:

12

At my workplace, we tend to use iostream, string, vector, map, and the odd algorithm or two. We haven't actually found many situations where template techniques were a best solution to a problem.

What I am looking for here are ideas, and optionally sample code that shows how you used a template technique to create a new solution to a problem that you encountered in real life.

As a bribe, expect an up vote for your answer.

+6  A: 

General info on templates:

Templates are useful anytime you need to use the same code but operating on different data types, where the types are known at compile time. And also when you have any kind of container object.

A very common usage is for just about every type of data structure. For example: Singly linked lists, doubly linked lists, trees, tries, hashtables, ...

Another very common usage is for sorting algorithms.

One of the main advantages of using templates is that you can remove code duplication. Code duplication is one of the biggest things you should avoid when programming.

You could implement a function Max as both a macro or a template, but the template implementation would be type safe and therefore better.

And now onto the cool stuff:

Also see template metaprogramming, which is a way of pre-evaluating code at compile-time rather than at run-time. Template metaprogramming has only immutable variables, and therefore its variables cannot change. Because of this template metaprogramming can be seen as a type of functional programming.

Check out this example of template metaprogramming from Wikipedia. It shows how templates can be used to execute code at compile time. Therefore at runtime you have a pre-calculated constant.

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}
Brian R. Bondy
So, you've used templates for factorials in real life?
Roddy
Not in real life :) But the idea can be expanded into many areas of real life.
Brian R. Bondy
So, do you have an example that you have PERSONALLY used?
Roddy
+4  A: 

I've used a lot of template code, mostly in Boost and the STL, but I've seldom had a need to write any.

One of the exceptions, a few years ago, was in a program that manipulated Windows PE-format EXE files. The company wanted to add 64-bit support, but the ExeFile class that I'd written to handle the files only worked with 32-bit ones. The code required to manipulate the 64-bit version was essentially identical, but it needed to use a different address type (64-bit instead of 32-bit), which caused two other data structures to be different as well.

Based on the STL's use of a single template to support both std::string and std::wstring, I decided to try making ExeFile a template, with the differing data structures and the address type as parameters. There were two places where I still had to use #ifdef WIN64 lines (slightly different processing requirements), but it wasn't really difficult to do. We've got full 32- and 64-bit support in that program now, and using the template means that every modification we've done since automatically applies to both versions.

Head Geek
Ya. excellent example. Thanks.
EvilTeach
+2  A: 

Templates I regulary consume are a multitude of container classes, boost smart pointers, scopeguards, a few STL algorithms.

Scenarios in which I have written templates:

  • custom containers
  • memory management, implementing type safety and CTor/DTor invocation on top of void * allocators
  • common implementation for overloads wiht different types, e.g.

    bool ContainsNan(float * , int) bool ContainsNan(double *, int)

which both just call a (local, hidden) helper function

template <typename T>
bool ContainsNanT<T>(T * values, int len) { ... actual code goes here } ;

Specific algorithms that are independent of the type, as long as the type has certain properties, e.g. binary serialization.

template <typename T>
void BinStream::Serialize(T & value) { ... }

// to make a type serializable, you need to implement
void SerializeElement(BinStream & strean, Foo & element);
void DeserializeElement(BinStream & stream, Foo & element)

Unlike virtual functions, templates allow more optimizations to take place.


Generally, templates allow to implement one concept or algorithm for a multitude of types, and have the differences resolved already at compile time.

peterchen
+1  A: 

I use templates to specify function object types. I often write code that takes a function object as an argument -- a function to integrate, a function to optimize, etc. -- and I find templates more convenient than inheritance. So my code receiving a function object -- such as an integrator or optimizer -- has a template parameter to specify the kind of function object it operates on.

John D. Cook
+5  A: 

One place that I do use templates to create my own code is to implement policy classes as described by Andrei Alexandrescu in Modern C++ Design. At present I'm working on a project that includes a set of classes that interact with BEA\h\h\h Oracle's Tuxedo TP monitor.

One facility that Tuxedo provides is transactional persistant queues, so I have a class TpQueue that interacts with the queue:

class TpQueue {
public:
   void enqueue(...)
   void dequeue(...)
   ...
}

However as the queue is transactional I need to decide what transaction behaviour I want; this could be done seperately outside of the TpQueue class but I think it's more explicit and less error prone if each TpQueue instance has its own policy on transactions. So I have a set of TransactionPolicy classes such as:

class OwnTransaction {
public:
   begin(...)  // Suspend any open transaction and start a new one
   commit(..)  // Commit my transaction and resume any suspended one
   abort(...)
}

class SharedTransaction {
public:
   begin(...)  // Join the currently active transaction or start a new one if there isn't one
   ...
}

And the TpQueue class gets re-written as

template <typename TXNPOLICY = SharedTransaction>
class TpQueue : public TXNPOLICY {
   ...
}

So inside TpQueue I can call begin(), abort(), commit() as needed but can change the behaviour based on the way I declare the instance:

TpQueue<SharedTransaction> queue1 ;
TpQueue<OwnTransaction> queue2 ;
Jackson
+2  A: 

We use COM and accept a pointer to an object that can either implement another interface directly or via IServiceProvider this prompted me to create this helper cast-like function.

// Get interface either via QueryInterface of via QueryService
template <class IFace>
CComPtr<IFace> GetIFace(IUnknown* unk)
{
    CComQIPtr<IFace> ret = unk; // Try QueryInterface
    if (ret == NULL) { // Fallback to QueryService
        if(CComQIPtr<IServiceProvider> ser = unk)
            ser->QueryService(__uuidof(IFace), __uuidof(IFace), (void**)&ret);
    }
    return ret;
}
Motti
+1  A: 

The obvious reasons (like preventing code-duplication by operating on different data types) aside, there is this really cool pattern that's called policy based design. I have asked a question about policies vs strategies.

Now, what's so nifty about this feature. Consider you are writing an interface for others to use. You know that your interface will be used, because it is a module in its own domain. But you don't know yet how people are going to use it. Policy-based design strengthens your code for future reuse; it makes you independent of data types a particular implementation relies on. The code is just "slurped in". :-)

Traits are per se a wonderful idea. They can attach particular behaviour, data and typedata to a model. Traits allow complete parameterization of all of these three fields. And the best of it, it's a very good way to make code reusable.

mstrobl
+4  A: 

I used templates (with the help of Boost.Fusion) to achieve type-safe integers for a hypergraph library that I was developing. I have a (hyper)edge ID and a vertex ID both of which are integers. With templates, vertex and hyperedge IDs became different types and using one when the other was expected generated a compile-time error. Saved me a lot of headache that I'd otherwise have with run-time debugging.

zvrba
Interesting. I've done something similar using inheritance of a base class with a single protected 'int' member, but a template class would have saved me a bit of code duplication if I'd thought of it!
Roddy
+2  A: 

Here's one example from a real project. I have getter functions like this:

bool getValue(wxString key, wxString& value);
bool getValue(wxString key, int& value);
bool getValue(wxString key, double& value);
bool getValue(wxString key, bool& value);
bool getValue(wxString key, StorageGranularity& value);
bool getValue(wxString key, std::vector<wxString>& value);

And then a variant with the 'default' value. It returns the value for key if it exists, or default value if it doesn't. Template saved me from having to create 6 new functions myself.

template <typename T>
T get(wxString key, const T& defaultValue)
{
    T temp;
    if (getValue(key, temp))
        return temp;
    else
        return defaultValue;
}
Milan Babuškov
too true. i did the exact same thing for wxConfig :)
Johannes Schaub - litb
+1  A: 

I once saw the following code:

void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
   // three lines of code
   callFunctionGeneric1(c) ;
   // three lines of code
}

repeated ten times:

void doSomethingGeneric2(SomeClass * c, SomeClass & d)
void doSomethingGeneric3(SomeClass * c, SomeClass & d)
void doSomethingGeneric4(SomeClass * c, SomeClass & d)
// Etc

Each function having the same 6 lines of code copy/pasted, and each time calling another function callFunctionGenericX with the same number suffix.

There were no way to refactor the whole thing altogether. So I kept the refactoring local.

I changed the code this way (from memory):

template<typename T>
void doSomethingGenericAnything(SomeClass * c, SomeClass & d, T t)
{
   // three lines of code
   t(c) ;
   // three lines of code
}

And modified the existing code with:

void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
   doSomethingGenericAnything(c, d, callFunctionGeneric1) ;
}

void doSomethingGeneric2(SomeClass * c, SomeClass & d)
{
   doSomethingGenericAnything(c, d, callFunctionGeneric2) ;
}

Etc.

This is somewhat highjacking the template thing, but in the end, I guess it's better than play with typedefed function pointers or using macros.

paercebal
+1  A: 

It's already been mentioned that you can use templates as policy classes to do something. I use this a lot.

I also use them, with the help of property maps (see boost site for more information on this), in order to access data in a generic way. This gives the opportunity to change the way you store data, without ever having to change the way you retrieve it.

Benoît
+1  A: 

I personally have used the Curiously Recurring Template Pattern as a means of enforcing some form of top-down design and bottom-up implementation. An example would be a specification for a generic handler where certain requirements on both form and interface are enforced on derived types at compile time. It looks something like this:

template <class Derived>
struct handler_base : Derived {
  void pre_call() {
    // do any universal pre_call handling here
    static_cast<Derived *>(this)->pre_call();
  };

  void post_call(typename Derived::result_type & result) {
    static_cast<Derived *>(this)->post_call(result);
    // do any universal post_call handling here
  };

  typename Derived::result_type
  operator() (typename Derived::arg_pack const & args) {
    pre_call();
    typename Derived::result_type temp = static_cast<Derived *>(this)->eval(args);
    post_call(temp);
    return temp;
  };

};

Something like this can be used then to make sure your handlers derive from this template and enforce top-down design and then allow for bottom-up customization:

struct my_handler : handler_base<my_handler> {
  typedef int result_type; // required to compile
  typedef tuple<int, int> arg_pack; // required to compile
  void pre_call(); // required to compile
  void post_call(int &); // required to compile
  int eval(arg_pack const &); // required to compile
};

This then allows you to have generic polymorphic functions that deal with only handler_base<> derived types:

template <class T, class Arg0, class Arg1>
typename T::result_type
invoke(handler_base<T> & handler, Arg0 const & arg0, Arg1 const & arg1) {
  return handler(make_tuple(arg0, arg1));
};
Dean Michael