MxM memory management
A fundamental construct for generic C++ memory management are smart pointers, like std::auto_ptr or boost::scoped_ptr for those familiar with the boost library.
Those constructs implements two concepts:
automatic release of memory during destruction
no support for copy / clone
of course, in two different ways: the boost::scoped_ptr is absolutely not copyable, so it is most used to keep a head-allocated object safe in a (stacked) scope of execution, while the std::auto_ptr implements that weird copy-semantic that lets a pointer flow through the call / return stack.
Within the MxM C++ picture, memory management assumes another facet, related to interoperability and neat call semantics: interoperability between engines, and between C++ native code and third party (mostly C) libraries - the neat semantics should help in the first case, while other tools would be welcome for the second.
Regardless of what the notorious(es) C++ FAQ (http://www.parashift.com/c++-faq-lite/) and FQA (http://yosefk.com/c++fqa/) tells us, we should know that C++ gives us two different kind of type modifiers to express pointers (i.e. syntactis objects to deal with runtime dynamic object types) : the * and the & (pointer and reference).
The first is usually tightly coupled in our minds with new and delete, the second to higher concepts like functions as lvalues or efficient pass-by-value. We know it would be really weird to receive a reference and try to delete it via its address, same way we would never try to delete a value on the stack.
In MxM, we rise that feeling at a semantic level, giving it the strongness of a best practice and library standard:
1) every pointer to object, returned or passed, implies that the receiver of the pointer is delegated of its management (i.e. its deallocation)
2) every reference to object, returned or passed, implies that the receiver must not try to manage the memory area associated with that object, who is either stack-managed or is member of another object
So a class will contain value-objects, object-pointers, object-references, or container of them, and it will never expose a pointer to any of them, but will instead return a value-copy, a fresh, heap-allocate, object-pointer copy, or a reference to an inner member, so that the receiver will never deallocate a memory area whose management is delegated to another subject - their owner.
All this works fine with “single” objects passed or received, but what about containers of objects ? How to avoid ambiguity when passing or returning a std::list< MXMObject * > ? Should we delete its content or not ? Should we pass a pointer to a list and return a reference to a list ? This would really overcomplicate container management, so we chose to add support for references into STL containers: this way, containers won’t modify the “single object” memory management semantics, so the deletion of pointers (even inside a container) is left to the receiver, and references (event inside a container) won’t be touched; we’re still free to pass or return containers themselves by reference depending on the specific case, and clearness, interface readability (that drives correctness of implementation) is safe and sound.
As we should know, STL containers of references are not possible, so we must add support for them, via a special kind of pointer-class.
Pointer types (MXMPointer.h)
In MxM we implement four pointer-classes, three of them related via inheritance:
1) the ptr::Shell
2) the ptr::Scoped
3) the ptr::Clonable
4) the ptr::Ref
the ptr::Shell is a base class for successive pointers and for those classes the coder wants to give automatic memory deletion for some kind of pointer. The ptr::Shell has a single constructor accepting a pointer to type (so, with explicit memory management delegation semantics), and five methods to manage it:
1) get: returns the raw value of the pointer
2) release: releases the pointer, so the shell will have no more notion or control of it
3) destroy: cleans the pointed memory (it’s the method called by the destructor)
4) detach: the shell will no more manage the memory, but it keeps the value of the address for successive use - this can be useful in some C-library integration scenario, to save some value-copy of the address
5) operator=: receives a new pointer to substitute the currently hold one, that is explicitly destroyed
the ptr::Scoped extends the shell adding two kinds of safe deference operator:
safe because they won’t allow segmentation faults if they know the memory they points to is 0 - they will raise exception instead.
A ptr::Scoped is not copyable, so it is suitable to carry pointers to abstract bases.
the ptr::Clonable extends the scoped one adding copy semantics via copy constructor of the type of the pointer it holds (indeed it’s name should be ptr::Copyable).
finally, the ptr::Ref is a reference shell (with no relations to other pointer types) to … other references: its constructors accepts a reference to an object, but the ptr::Ref stores the address of the referred object instead of the reference directly: this allows us to re-assign the ptr::Ref using it’s assignment operator.
we can access the “reference” inside it via the get method or the cast-to-referenced-type operator. The ptr::Ref is copyable, since copy involved duplication of an address, so it is suitable to be put inside a STL container.
An utility function is introduced, to obtain a reference from a pointer: the ptr::toRef function template.
We don’t have to feel satisfied, yet: those pointer types works well with types managed by the new / delete operator couple, but what if (when) we have to deal with some C library, full of structs and per-struct, specifics, malloc / free functions ? different names and musts for every kind of struct (if you ever opened OpenSSL you know what I mean). One would argue that templates can be specialized, but the cost of specializing a whole pointer class is high, multiplied for the variety of just a single library, in terms of source files and lines-per-source. So we decided to decouple just the very cleanup routine within another function template, detached from the class: the ptr::clear template functions, that, in its default implementation, deletes the pointer it receives: when specialized, it will do whatever we need, but usually it will call some struct_free C function. The ptr::Shell will rely on a call to ptr::clear when destroying the object it holds, to ensure proper deallocation using the required function specialization when needed - and not only the Shell, but every of its extensions.
Same thought is followed about object copy, with ptr::clone, that in its default implementation calls type’s copy constructor.
Container management (MXMContainer.h)
At this point, we have the tools to pass container of pointers (no tool is needed) or containers of references to objects, using ptr::Ref, but obviously it’s not enough (if we like to code less and do little or no mistakes). Relying on ptr::clear, ptr::clone and ptr::toRef, we can easily build template functions to clean up, clone or convert to reference entire containers of pointers, specifically every kind of plain container (list, vector, deque, …) and the map (usually a value-to-object-pointer map). Specializations of the basic (and obvious) implementation of such template functions (ctr::clear, ctr::clone and ctr::toRef) joint to the template pattern-magic, will give us the ability to clean, clone or reference containers of containers (of containers …) of pointers (like list< list< T * > >, map< K, list< T* > >, and so on), always using the same syntax.