Making Objects Polymorphic without Intrusion
It is generally assumed that in C++, in order to achieve run-time polymorphic behavior, we have to inherit from a base class with virtual functions. Java, and other languages also provide us with interfaces, but the classes have to explicitly state their intention to implement the interface.
This is a misconception that a lot of people understandably have when they first hear about the C++ Object Oriented Template Library (OOTL). In the September 2004 issue of the C/C++ Users Journal, I outline a technique in C++ for implementing interface reference types without requiring changes to existing objects. This technique is used in the upcoming Boost Interfaces Library (BIL) by Jonathan Turkanis. The BIL is not yet ready for a public beta release, but a pre-release version has been included with the OOTL version 0.1.
You can download the latest OOTL through SourceForge here but you will need the latest Boost library version 1.32.0 in order to compile it, which you can also get through SourceForge here.
Given two existing classes such as:
class Faz {
public:
int Fu() { return 1; }
void Bar(int x) { printf("faz %d\n", x); }
}
class Baz {
public:
int Fu() { return 2; }
void Bar(int x) { printf("baz %d\n", x); }
}
The BIL can then be used to express an interface after these classes have been defined elsewhere as follows:
BOOST_IDL_BEGIN(IFuBar)
BOOST_IDL_FN0(Fu, int)
BOOST_IDL_FN1(Bar, void, (int, x))
BOOST_IDL_END(IFuBar)
You will have to put the blame for the ugly syntax on C++, not Jonathan. C++0x will surely enable improved syntax, especially if Jonathan or myself have anything to say about it (in fact, I would like to see interface references gain first class status). The BOOST_
prefix, is a requirement for consideration in Boost, which clearly is the eventual goal of the BIL.
Once we define this interface, after the fact, we can use it to make Faz
and Baz
, polymorphic. Consider the following:
int main{
faz f;
baz b;
IFuBar i;
if ((rnd() % 2) == 0) {
i = f;
} else {
i = b;
}
i.Bar(i.Fu());
}
This code snippet prints out either faz 1
or baz 2
.
And that's all I have to say about that.