why I hate c++, chapter 1
So, I have this C++ program with a class that among its other responsibilities needs to interpret textual commands from a user.
class BallOfMud {
...
struct command {
string command_name;
string arg_spec;
bool (BallOfMud::*func)(stuff);
};
...
};
The command implementation functions are represented here as pointer-to-member so that they can act on the relevant class instance.
I find that the class has grown too big and diffuse, and decide
to split it into two more coherent ones. Each of the two will
be responsible for handling some of the commands. Time to split
off a CommandInterpreter class, then.
Spotted the problem yet? No? Well...
class CommandInterpreter {
...
struct command {
string command_name;
string arg_spec;
bool (CommandInterpreter::*func)(stuff);
};
...
};
class PerfectlyFormedJewel : public CommandInterpreter { ... };
Now C++'s type soundness rules say that you can't store a
PerfectlyFormedJewel::* in a space designed for a
CommandInterpreter::*. Why? Because the latter has
to be able to do the right thing when called with any instance
of CommandInterpreter pointed to by this,
whereas the former might rely on being given an instance of
PerfectlyFormedJewel. Aargh!
The "solution" (familiar to old C++ hands) is "Coplien's curiously recurring template pattern", whereby an instantiation of a class template can have one of its own subclasses as a template parameter:
template<typename T>
class CommandInterpreter {
struct command {
bool (T::*func)(...);
};
};
class PerfectlyFormedJewel : public CommandInterpreter<PerfectlyFormedJewel> { ... };
Only you really want to make CommandInterpreter<T>
inherit (excuse me, derive; this is C++, and it would never do to use standard
terminology) from a non-templated superbase class, so that
you don't have to put all the implementation of the damn thing
into the public interface. (And also, in this particular case, for other
reasons I shan't go into.) And don't get me started on what happens when
you want one of these perfectly formed jewels to have subclasses of its
own.
The tragic thing is that once C++ programmers are really well
house-trained, their reaction on first seeing something like this
is "Cool! A templated class can have one of its own subclasses
as a template parameter! Just think of all the
confusingsophisticated things
we can do with that!", when it ought to be "What kind of idiot
language requires you to put up with this kind of nonsense?".