C++ containers like std::vector
, std::list
, std::map
, std::unordered_map
are non-polymorphic by design, first of all by relying on template member functions. These cannot be virtual and therefore cannot be overridden in a polymorphic way.
This first limitation wouldn't necessarily stop us from adding new behaviour (i.e. new member functions) to container subclasses in a polymorphic way, while still exposing the well known non-polymorphic container interfaces. Just refrain from overriding the non-virtuals!
Another limitation, standard containers' lack of virtual destructors, is a show stopper. We can easily subclass C++ containers directly. When such an object is deleted via a base class pointer, however, bad things happen, ranging from memory leaks to heap corruption.
is very simple but effective. They
- compose C++ container class template delegates with the same set of template parameters,
- provide 1-by-1 forwarding non-virtual inline members and member templates, reference type conversions, constructors, assignment operators, and
- add a virtual destructor!
- Can be used almost interchangeably with C++11 standard containers.
- Implicit conversions allow transparent substitution of one for the other almost everywhere, except as pointer targets. (Note: delegate type rvalue reference conversions are explicit.)
- Move semantics, rvalue references.
Decorator pattern is cool and old school. Typing hundreds of forwarding members is neither cool nor fun, but a one time task. Fortunately, C++ library standards, while evolving, do not alter signatures of existing public container members.
None, assuming compilers manage to collapse inline decorator members into the delegate container operations.
Adds the cost for setting the hidden vtable pointer per decorator instance on creation, and the check for subclass destructors on deletion.
Adds one hidden vtable pointer per decorator instance, compared to the raw container.
The Polymorphic package is harnessed with a set of test suite templates located in namespace Testee
. Main focus is template member signatures and return types, but also includes basic plausibility checks of results.
Test suites are not based on any particular framework. With test cases being self contained it is amazing how far you can get with template templates, a plain std::list<TestCase>
, initializer lists and lambda expressions.
On the the other hand, using test suite templates nicely solved the "test-the-tester" challenge. Running tests first against the delegates, i.e. the C++ standard library implementations, helped developing and verifying test suites. Finally, these were redirected to verify decorators are providing the same APIs as delegates do.
Polymorphic decorators are a headers-only library. You may just copy the include/Polymorphic
directory into a location found by your build system.
CMake is required to install headers in a location along with CMake export files (recommended), and to build and run the test suites.
./configure [--prefix=/path/to/Polymorphic]
make
make check
make install
The default installation prefix is /usr/local
. The step make check
, building and running test suites, is optional.
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=C:/path/to/Polymorphic
There is no default for CMAKE_INSTALL_PREFIX
on Windows. Commands above will create a Visual Studio solution with an INSTALL
and a check
project.
Note: When using Visual Studio 2013 please configure the Visual C++ Compiler November 2013 CTP toolset in order to get support for noexcept
. Adding the option -TCTP_Nov2013
to the cmake command line will then select the appropriate compiler.
Free Software, licensed under the Boost Software License.