When a language is used over a long period of time patterns of good usage emerge. When they are a whole application level they are called architectural patterns. When they concern a component they are called
design patterns. When they are about a few lines they are called idioms (if I remember correctly I saw this idea first in Andrei Alexandrescu's
MC++D). Sometimes these patterns are included in future language designs and this takes the form of an apparent limitation on the old, more flexible, language. An example is the transition from C++ to Java.
I'd like to present here in a few words a C++ idiom. It is call the "e;hoisting"e; idiom. The idea is very simple:
whenever a piece of code can be shared do so; don't repeat it, even if you don't do it manually. IMO the last part, "e;even if you don't do it manually"e;, is applicable in two situations: (1) when you want to avoid relying on such tools, and (2) when the tools don't do the replication in a "e;smart"e; way.
Templates in C++ turn the compiler in a veritable code replicating machine. Their syntax is verbose (compared to functional languages). However they are a very good productive tool. They also tend to speed up run times because part of computation is moved to the compilation phase. For example you can use it to precompute look-up tables at compile time:
template <int N>
class Fibonacci
{
public:
static const int value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};
template <>
class Fibonacci<1>
{
public:
static const int value = 1;
};
template <>
class Fibonacci<0>
{
public:
static const int value = 0;
};
int fib_lookup[10] = {
Fibonacci<0>::value,
Fibonacci<1>::value,
Fibonacci<2>::value,
Fibonacci<3>::value,
Fibonacci<4>::value,
Fibonacci<5>::value,
Fibonacci<6>::value,
Fibonacci<7>::value,
Fibonacci<8>::value,
Fibonacci<9>::value; };
(after this and my last posts you might think that I'm obsessed with fibonacci numbers; I'm not :) )
But there are also problems. It happens that very often templates lead to code bloat. By this I mean that the executable gets very large. Compilers are not very smart today to be able to replicate only what is necessary and share code between different template instantiations. Instead you need to do it by hand. A common situation where this is needed is when different data types share the same machine represantation. This is the case with pointers: T* will always be represented like S* no matter what T and S are. It also happens for int and long for most machines.
The idea is to specialize the template for one of the types and to use this specialization as a base class:
template <typename T>
class vector {
// general implementation
};
template <>
class vector<void*> {
// implementation for void* (possibly with optimizations)
};
template <typename T>
class vector<T*> : public vector<void*> {
// delegate most of the functions to base class
};
Perhaps I'll give more details in a subsequent post. Anyway, in the meantime you might want to search for posts on the subject from Francis Glassborrow (a member of C++ Standard Comitee) in this
group.
No comments:
Post a Comment
Note: (1) You need to have third-party cookies enabled in order to comment on Blogger. (2) Better to copy your comment before hitting publish/preview. Blogger sometimes eats comments on the first try, but the second works. Crazy Blogger.