I recently implemented smart pointers into the FTL and was looking for a similar set to Boost’s own due to their wide spread use. Obviously there are a few ways in which smart pointers can be implemented and I thought it would be interesting to document the reasons why I followed a particular route rather than the most obvious (and some would say simplest) method.
Due to the complexities of shared pointers (shared_ptr and weak_ptr etc.) it made sense to start with simply non-copyable pointers which would allow me to nail down the design process before moving on so I ended up with the following specification (obviously based on Boost’s specification)
simple_ptr – A non-copyable pointer with no garbage collection
simple_array – A non-copyable pointer to array with no garbage collection
scoped_ptr – A non-copyable pointer with automatic garbage collection when the pointer leaves scope
scoped_array – A non-copyable pointer to array with automatic garbage collection when the pointer leaves scope
I did originally debate the need for ‘array’ versions of these pointers instead hoping people would prefer the FTL vector or array, which would obviously be much safer than standard C arrays. But very often people are used to working with memory and need to work at the lowest level possible. Not giving the _array option will just limit the use of the smart pointers, which will turn people away from using them and generally lead to more unsafe code.
Boost Style Pointers
The initial approach was to look at ‘Boost Style’ pointers which in other words are individual, well thought-out classes build for a specific purpose. Each class is defined and created manually, taking an individual template which specifies the type contained within the pointer.
The good thing about this approach is that the template class is as simple as they come, generally only containing one type member and a couple of access functions. Any other code will act as if this class wasn’t templated and is very easy to read and understand. Also related to the simplicity of the template is that it is pretty much guaranteed to compile on any platforms that have template support (which probably covers 99.9% of mature C++ compilers and certainly the ones we work with every day).
Unfortunately its simplicity is also its main disadvantage. Each class (unless you want to use an inheritance approach) is a self contained unit, which means there is a lot of duplication between the different classes. Operator overloads, memory management etc. will be the same in multiple classes and while the classes can be quite small it does mean fixes need to be made more than once which can lead to copy-and-paste errors. Unit Testing is also constantly duplicated as you have to test the same code in different classes again and again, doubling the number of changes you need to make for even a simple fix.
It also means that if someone wants to extend a pointer for a specific case they will have to manually create their pointer specification due to the base pointers containing no virtual functions and not supporting inheritance.
It was due to these problems that made me move away from this method. The rigid structure that they bring to the table and the way projects cannot easily create their own versions lead me to think it would cause frustration when doing anything other than using them as simple pointers.
Template Method Specialisation
To try and limit the amount of duplicated code I looked at how some of the functions that were different could be wrapped up in the same class. The simplest way to do this would be to use a take on the Template Method Pattern which is designed to provide a class that has 99% the same behaviour but has some elements that needs to behave slightly different.
Using this would allow me to wrap up the _ptr and _array versions of a pointer into the same class and halving the amount of duplication needed to have multiple types.
template<typename T, bool Array = false>
delete  m_ptr;
ftl::ptr::simple_ptr_type<int, true> aSimpleArray;
This would be an elegant solution which gives the class a bit more flexibility though care would have to be taken to pass in the correct type when declaring them. We could make this clearer by defining an enum and using this as the templated type
and taking that further, creating a typedef for each pointer type which makes it even clearer (see the next section for problems with this…)
typedef simple_ptr_type<T, EPointerType_Ptr> simple_ptr;
typedef simple_ptr_type<T, EPointerType_Array> simple_array;
So far so good and this cut down on the main problem we hit when using the initial implementation. The second template could be quite misleading and requires people to investigate the class to figure out exactly what it’s for but that’s not exactly a serious drawback due to the solutions I’ve already covered.
The main problem comes from the additional template type that is used to decide which method to call. On some platforms the compiler was smart enough to only compile what was used for each templated type. On others it compiled all the code regardless, which means that if a particular branch was illegal with a given templated type, it would still cause compiler errors even if it was never used.
Even with those problems I did see the advantages to this but also that it could lead down the following path
template<typename T, bool Array = false, bool Scoped = false, …> class simple_ptr_type
So while having the pointer customisable with various templated types could have it’s uses, it would start to become quite unmanageable (and confusing) and in the end not actually all that extendible since you could only switch it one way or the other.
Policy Based Ideas
It was around this time that I finally received a copy of Modern C++ Design by Andrei Alexandrescu. In one section he discussed a policy design approach to programming where-as the templated types actually provide specific behaviour within a fixed scope. This started to ring bells with what I was moving towards during the previous implementation where the templates would allow me to customise a ‘basic’ pointer type but also allow others to create the pointers they needed.
So I quickly moved forwards and ended up with something similar to the following
template< typename T,
Now obviously this is a lot of templates and a lot of work to understand – especially when you come to it cold. So it made sense to provide various objects that would come as stock polices that people could put together to create a range of smart pointers suited to their needs. These policies would also provide the default interface that others could build from.
So as an example of how these policies can be used in the smart pointer object
const_reference operator*(void) const
// This allows the user to provide
// additional checks when dereferencing
// and then return the pointer type as
// a reference (you may be using multiple
// pointer types which need to be manually
// checked before parts of it are returned)
CheckingPolicy::on_dereference( m_storageValue );
// Here the deallocation policy can simply
// call ‘delete’ or ‘delete ’ or maybe does
// something more extensive with heap pools etc.
DeallocationPolicy::deallocate_memory( m_storageValue );
It was a conscious decision to make the individual polices static member functions rather than internal or inherited objects to reduce the size of the pointer. If it was to use a component based system where the pointer contained individual objects then the size of the pointer could bloat quite quickly and it would also lead towards inheritance based solutions to other pointer types which is something I wanted to avoid.
You’ll also notice that the pointer type used in the example has the post-fix ‘_noncopyable’ which obviously leads to another type called ‘smart_pointer_copyable’. It would have been ideal to have a single pointer template and the policies themselves dictate which can be copied and which cannot (which is the whole idea after all!). This is something I initially looked into where the pointer object’s copy constructor and =operator used polices themselves and these denied copying or asserted when they were used. Unfortunately this meant that we couldn’t use a compile time check (we would have to wait until the operator was actually used) for the same reasons the template method couldn’t be used (some compilers compiling the whole file regardless of use). So while this still leads to a bit of code duplication between the copy and non-copy variants it still limits it to the structure rather than the actual implementation of the polices.
Obviously this is all good and well, but it would be a colossal pain if we had to manually list all the policies (even if we provided defaults it would still be difficult to change the odd policy). Typedef’s should be the answer to this so we can manually provide the template type, but the template provides the rest of the policies.
For example (namespaces removed to make it readable on here):
typedef smart_ptr<T> simple_pointer;
typedef smart_ptr<T, policy::DeleteArray> simple_array;
Which would just be
Unfortunately, templated typedefs are not part of the C++ standard (but hopefully this feature will be added sometime in the future), so the only other option was to do the following…
template <typename T>
template <typename T>
Which finally leads to
I really cannot take credit for that little gem and it was a relief when I found a solution otherwise this was yet another implementation that would have been so close but just not good enough.
So finally we have a nice way of creating basic smart pointer types that allows any other user to customise their pointers to make them ideal for whatever situation they find themselves in. While the static policy based approach can limit what individual pointers can do it still provides a wide range of extensibility without over-bloating the pointer.