Monday, April 13, 2009

A Plethora of Paradigms

paradigm - a pretentious and overused term for a way of thinking (Bjarne Stroustrup, "C++ Glossary")

C++ is frequently described (by Stroustrup and others) as a multi-paradigm programming language, which simply means that it lets you use more than one programming paradigm (such as procedural or object-oriented) and freely switch between or combine paradigms. When I was first introduced to this concept of multi-paradigm programming languages, I thought that it was an incredibly cool trait of C++. Then I read on Wikipedia that the multi-paradigm label can be applied to many more languages than I'd realized and that C++ is a relative lightweight at only three paradigms. (The current record holder, supporting eight Wikipedian-recognized paradigms, is a language I've never heard of called Oz.) However, C++ is probably still the best known example of this sort - there are even ">books about it - and has some of the most practical multi-paradigm applications. A quick overview of C++'s paradigms:

First, of course, is the structural or procedural programming paradigm. Procedural programming's pretty passé, so there's not much to be said here, except to note that C++'s multi-paradigm nature lets you incrementally migrate a procedural app to an OO design.

Second is object-oriented programming. When OO first went mainstream, it was billed as a way of achieving reuse in software development. This doesn't seem to have quite worked out as advertised; creating truly reusable software is hard regardless of the technology you're using, and while OO design is critical to frameworks from MFC to RoR, framework development isn't where most programmers live. Even if its ability to grant reuse per se is limited, modern OO design is quite good at permitting software to be flexible, extensible, and stable in the face of change. "Modern OO design" means taking full advantage of guidelines like the SOLID principles and the Law of Demeter rather than just applying the basic OO techniques of encapsulation, inheritance, and polymorphism. It also includes knowing when to bend those guidelines if circumstances call for it.

C++'s third paradigm is generic programming. I find this to be harder to get a handle on... It's much less commonly taught than object-oriented programming, and although I've worked in C++ for years, it's only been in the last year or two that I've really started to "get" generic programming. (A good test to see how well you understand generic programming is to browse the Boost libraries and see how many of them you think you could, in theory, implement.) Generic programming makes the STL possible; it makes it possible to add features like dimensional analysis, lambda expressions, and callbacks to C++; it's a major component in adding user-defined types of all sorts. It also creates some rather heinous error messages...

However, generic programming isn't just for STL containers and the metaprogramming wizards at Boost; it overlaps with and can partially replace OO techniques. OO programming is known for permitting software components to be loosely coupled (such that they can be freely combined and freely changed, within limits, without the effects of those changes rippling out to other components), through polymorphism and interfaces. Generic programming also permits loose coupling, except that it's handled entirely at compile time, with no runtime overhead, and it doesn't require that objects be related through an inheritance hierarchy. The Curiously Recurring Template Pattern is probably the simplest and best known example of this (and is even referred to as compile-time or static polymorphism), but it's far from the only example. Most of part four, for example, of Imperfect C++ contains different applications of generic programming techniques to simulate what might require inheritance, interfaces, or (C#) extension methods in the OO world.

For even more power, the generic and OO paradigms can be combined. And because C++ is such a flexible language, you're not limited to the "officially supported" paradigms. I've already touched on Boost's support for functional programming; as another example, reflection (which is available through vendor extensions, like C++Builder's or the .NET Framework's and through third party libraries like CERN's Reflex) can also promote flexible, extensible software. Having three different techniques from three different paradigms that can be used individually or in various combinations offers incredible power: power to shoot yourself in the foot by creating an overcomplicated, obtuse rat's nest of code, or power to write robust, extensible software. Now if I could only figure out which category my code falls in...

Tuesday, April 7, 2009

Things I Didn't Know about C++: Template Template Parameters

Continuing my series on things I didn't know about C++...

I knew about templates, and I knew about parameters to templates, but I did not know about template template parameters, which are useful when dealing with containers and policies and, if nothing else, are worth learning about for having such a "pleasingly repetitious name" (in C++ author Stephen Dewhurst's words).

A template template parameter is simply a template parameter that is itself a template. Dewhurst gives the example of a Stack template that can be customized to use a particular underlying storage container:

template <typename T, template <typename> class Cont>
class Stack;

Stack<int, List> stack_of_ints;

Without template template parameters, the base type (int in this case), which hampers readability at best and could be a source of errors at worst:

template <typename T, typename Cont>
class Stack;

Stack<int, List<int> > stack_of_ints;
Stack<double, List<int> > stack_of_doubles_badly_truncated_to_ints;

Template template parameters can be used anywhere you need to let the caller customize an algorithm or class for a particular container, policy, or similar:

  • A stack or other higher-level container can be instantiated using any lower-level container, as in Dewhurst's example.
  • A class or function can be customized with a policy template. Andrei Alexandrescu gives an example of this in section 1.5 of Modern C++ Design. (His example is also summarized in this Stack Overflow post.)
  • Template template parameters can be used when you need to know both the type of a template and how that template was instantiated, although this comes up less often than you might initially think, because there are often better ways to find the types involved. For example, there's no need to declare template <typename T, template <typename> class Cont> void ProcessContainedObjects(Cont<T>& cont) to let ProcessContainedObjects know what T is when any well-written container will define the Cont::value_type typedef.

Template template parameters are a useful addition to the C++ template programming toolbox, alongside techniques such as partial template specialization, type traits, concepts (also in C++0x), and C++0x's variadic templates (which can be simulated in C++03 to some extent by using default template arguments or by using Boost Preprocessor to create overloads of every arity from 1 through a #define'd limit).