Memory Component¶
This section discusses the memory component of MNMLSTC Core. Contained in this component are three new smart pointers (two with deep copy semantics) and a C++11 equivalent to C++14’s make_unique<T>().
Polymorphic Smart Pointer¶
- class poly_ptr<T, Deleter>¶
The poly_ptr is a smart pointer for polymorphic types that retains sole ownership of the polymorphic type T and performs deep copies on assignment or copy construction. This use is extremely close to the any, however it affords the user the ability to ascertain what possible types the poly_ptr has while also allowing custom allocation, copying, and deallocation. In this way, a poly_ptr can be seen as a std::unique_ptr with deep-copy semantics, and type erasure. poly_ptr has an interface equivalent to std::unique_ptr. Due to the type erasure that is used, the size of a poly_ptr is sizeof(std::unique_ptr<T, Deleter> + sizeof(function-ptr).
Note
Due to the lack of polymorphic allocators in C++, custom memory allocation is currently relegated to performing allocation within a user-supplied copier function. Because the copier function is a function pointer this means that poly_ptr is restricted to stateless lambdas or function pointers.
The poly_ptr requires that RTTI and exceptions be enabled. Because of the type-erasure used, RTTI is absolutely required to for the polymorphic deep-copy to take place.
Warning
Only a polymorphic type (that is, any type where the std::is_polymorphic type trait is true) may be used with poly_ptr. If deep-copy semantics with a std::unique_ptr are desired for a non-polymorphic type, use deep_ptr instead.
The poly_ptr is not polymorphic itself and is marked final to prevent user inheritance.
- type unique_type¶
Represents the std::unique_ptr used internally to hold the managed object.
- type element_type¶
A type equivalent to the expression typename unique_type::element_type
- type deleter_type¶
A type equivalent to the expression typename unique_type::deleter_type
- type copier_type¶
A type used to represent the copy function used to perform deep copies. It has the type signature of: unique_type (*)(unique_type const&).
- type pointer¶
A type equivalent to the expression typename unique_type::pointer.
- explicit poly_ptr(U* ptr)¶
Takes a derived pointer to a given type U. U must be a non-abstract type and have element_type as a base class within its inheritance tree.
- poly_ptr(U*, E&&, copier_type=default_poly_copy)¶
Takes some derived type U, a universal reference E, and an optional copier function pointer. The universal reference E is forwarded to the internal std::unique_ptr, where it will handle the proper rules required to initialize the deleter_type.
- explicit poly_ptr(std::unique_ptr<U, E>&&, copier_type)¶
A unique_ptr of type U and deleter E, with an optional copier_type parameter that is by default default_poly_copy<T, D, U>(). As normal, U must have element_type in its hierarchy.
- poly_ptr(poly_ptr const& that)¶
Performs a deep copy with the object managed by that, if any such object exists.
- poly_ptr(polymorphic&& that)¶
Moves that‘s pointer and copier into *this, and then sets that‘s copier to null_poly_copy<T, D>().
- poly_ptr() noexcept¶
The default constructor for a poly_ptr will place it into such a state that bool(poly_ptr) will return false;
- operator=(std::unique_ptr<U, D>&& ptr)¶
Calls poly_ptr<T, Deleter> { std::move(ptr) }.swap(*this)
Returns: *this
- operator=(poly_ptr<T, Deleter>&& that) noexcept¶
Calls poly_ptr<T, Deleter> { std::move(that) }.swap(*this)
Returns: *this
- operator=(poly_ptr<T, Deleter> const& that)¶
Performs a deep copy with the object managed by that, if such an object exists.
Returns: *this
- operator bool() const noexcept¶
Note
This cast operator is marked as explicit.
Returns: Whether *this owns an object
- element_type& operator*() const¶
Returns: an lvalue reference to the object owned by *this.
- pointer get() const noexcept¶
Returns: A pointer to the managed object, or nullptr if no such object exists.
- deleter_type const& get_deleter() const noexcept¶
- deleter_type& get_deleter() noexcept¶
Returns: The deleter object used for destruction of the managed object.
- copier_type const& get_copier() const noexcept¶
- copier_type& get_copier() noexcept¶
Returns: The function pointer used for copying the managed object.
- pointer release() noexcept¶
Releases the ownership of the managed object, if any such object exists. Any calls to poly_ptr<T, Deleter>::get() will return nullptr after this call.
Returns: pointer to the managed object or nullptr if the poly_ptr did not manage an object.
- void reset(pointer ptr=pointer { })¶
Replaces the managed object. Performs the following actions (these differ from the order of operations followed by std::unique_ptr).
- If the incoming pointer is nullptr, the order of operations follows those performed by std::unique_ptr, along with the value returned by poly_ptr<T, Deleter>::get_copier() being set to a null copier.
- If the incoming pointer is not nullptr, and there is no managed object, a bad_polymorphic_reset exception is thrown.
- If the incoming pointer is not nullptr, a typeid comparison between the managed object and the incoming pointer is performed. If the std::type_info returned from both is not identical, a bad_polymorphic_reset is thrown. If the std::type_info is identical, the order of operations follows those performed by std::unique_ptr.
- void swap(poly_ptr&) noexcept¶
Swaps the managed object and copier function
Deep Copying Smart Pointer¶
- class deep_ptr<T, Deleter, Copier>¶
deep_ptr is a smart pointer for a type that retains sole ownership of the pointer it manages and performs a deep copy on assignment or copy construction. deep_ptr is much like std::unique_ptr with deep-copy semantics. Unlike poly_ptr, deep_ptr is for concrete types where polymorphism is not desired. poly_ptr has some storage overhead for copying a polymorphic type, however deep_ptr performs the same optimization as std::unique_ptr in that it is only sizeof(T*), unless the given Deleter and Copier types hold state.
With the exception of the copy assignment and copy constructor, deep_ptr has an interface identical to that of std::unique_ptr, and exhibits the same behavior as std::unique_ptr
If the result of the copier_type differs from pointer, the program will be malformed, and a static assertion will cause a compiler error.
- type deleter_type¶
The deleter object used to destroy and deallocate the object managed by the deep_ptr.
- type copier_type¶
The copier object used to perform an allocation and deep copy the object managed by deep_ptr.
- type pointer¶
remove_reference_t<deleter_type>::pointer if the type exists, otherwise, element_type*.
- deep_ptr(pointer ptr, E&& deleter, C&& copier) noexcept¶
Actually two separate constructors, these follow the behavior of the std::unique_ptr constructors that take a pointer, and deleter object. The behavior extends to the type desired for the copier object as well.
- deep_ptr(std::unique_ptr<U, E>&&) noexcept¶
Constructs a deep_ptr with the contents of the unique_ptr. The given type U must be a pointer convertible to pointer, and E must be a type that can construct a deleter_type.
- explicit deep_ptr(pointer ptr) noexcept¶
Constructs a deep_ptr with the default deleter, default copier, and the given pointer. The deep_ptr assumes ownership of ptr.
- deep_ptr(std::nullptr_t) noexcept
Delegates construction of the deep_ptr to the default constructor.
- deep_ptr(deep_ptr const& that)¶
Constructs a new object to be managed via that‘s object.
- deep_ptr(deep_ptr&& that) noexcept¶
Constructs a deep_ptr with the managed object, deleter, and copier of that via move construction.
Postcondition: that is empty
- deep_ptr& operator=(std::unique_ptr<U, D>&& ptr) noexcept¶
Assigns the contents of ptr to *this
- deep_ptr& operator=(deep_ptr const&) noexcept¶
- deep_ptr& operator=(deep_ptr&&) noexcept
Assigns the contents of the incoming deep_ptr to *this
- deep_ptr& operator=(std::nullptr_t) noexcept
Resets the deep_ptr and the object it manages.
- operator bool() const noexcept¶
Note
This cast operator is marked as explicit
Returns: Whether the deep_ptr manages an object
- element_type& operator*() const¶
Attempting to dereference a deep_ptr that does not manage an object will result in undefined behavior
Returns: an lvalue reference to the managed object
- pointer operator->() const noexcept¶
Returns: a pointer to the managed object or nullptr if no such object exists.
- pointer get() const noexcept¶
Returns: A pointer to the managed object, or nullptr if no such object exists.
- deleter_type const& get_deleter() const noexcept¶
- deleter_type& get_deleter() noexcept¶
Returns: The deleter object used for destruction of the managed object.
- copier_type const& get_copier() const noexcept¶
- copier_type& get_copier() noexcept¶
Returns: The copier object used for copying the managed object.
- pointer release() noexcept¶
Postcondition: deep_ptr<T, Deleter, Copier>::get() returns nullptr Releases the ownership of the managed object, if any such object exists.
- void swap(deep_ptr&) noexcept¶
Swaps the managed object, copier object, and deleter object.
Dumbest Smart Pointer¶
- class observer_ptr<T>¶
observer_ptr is “the dumbest smart pointer”, in that it is only ever used in the place of a raw pointer. The idea is to inform the user that the observer_ptr does not own the pointer it watches. It can be treated like a raw pointer, except that there is no need to read the documentation to see if the user needs to manage a raw pointer or not. Because the observer_ptr is a non-owning smart pointer, the need for a move constructor and assignment operator is superfluous as copying a pointer is just as cheap as moving one.
- type element_type¶
The type of the object managed by observer_ptr.
- type const_pointer¶
- type pointer¶
add_pointer_t<add_const_t<element_type> and add_pointer_t<element_type> respectively.
- type const_reference¶
- type reference¶
add_lvalue_reference<add_const_t<element_type> and add_lvalue_reference<element_type> respectively.
- observer_ptr(std::nullptr_t ptr)¶
- observer_ptr(pointer ptr)¶
- observer_ptr(add_pointer_t<T> ptr)¶
Constructs the observer_ptr with the given pointer. If ptr is convertible to observer_ptr<T>::pointer, it will construct it that way (via a dynamic_cast).
- void swap(observer_ptr<T>&)¶
Swaps the contents of the observer_ptr with the other.
- operator const_pointer() const¶
- operator pointer()¶
Noexcept: true Explicit: Yes Allows an observer_ptr to be explicitly converted to observer_ptr<T>::const_pointer or observer_ptr<T>::pointer respectively.
- operator bool() const¶
Noexcept: true Explicit: Yes Allows the observer_ptr to be explicitly converted to a boolean.
- reference operator*() const¶
Noexcept: true Returns: reference to the object watched by the observer_ptr.
- pointer operator->() const¶
Noexcept: true Returns: the object watched by the observer_ptr
- pointer get() const¶
Noexcept: true Returns: The object watched by the observer_ptr
- pointer release() noexcept¶
Noexcept: true Returns: the object watched by the observer_ptr. The observer_ptr is then set to nullptr.
- void reset(pointer ptr=nullptr)¶
Noexcept: true Resets the object watched by the observer_ptr with ptr.
Utilities¶
- class bad_polymorphic_reset¶
Inherits: std::logic_error Thrown when a poly_ptr<T, Deleter>::reset() is passed a non-null pointer and the poly_ptr does not manage an object, or if the passed in pointer differs in type from the currently managed object.
- class default_copy<T>¶
The default copy policy used by deep_ptr during a copy operation. There are no partial specializations available. The default operation to perform is to allocate a new T pointer with operator new, and to initialize this T with a T const&.
- type pointer¶
Represents T*
- constexpr default_copy()¶
Constructs the default_copy<T> object.
- default_copy(default_copy<U> const&) noexcept¶
Constructs a default_copy<T> from another default_copy<T>.
- pointer operator()(pointer const ptr)¶
Allocates a new pointer and initializes it with the dereferenced ptr, to invoke the copy constructor.
- std::unique_ptr<T, D> default_poly_copy<T, D, U>(std::unique_ptr<T, D> const&)¶
This function is used as the default copier when assigning a raw pointer or unique_ptr to a poly_ptr. It will perform a deep copy with a call to make_unique, with type U and dynamic_cast the stored pointer of T into U as it performs the assignment. The deleter_type of the given unique_ptr will also be copied.
Returns: std::unique_ptr<T, D> with a managed object.
Comparison Operators¶
- bool operator==(poly_ptr const&, poly_ptr const&) noexcept¶
- bool operator!=(poly_ptr const&, poly_ptr const&) noexcept¶
- bool operator>=(poly_ptr const&, poly_ptr const&) noexcept¶
- bool operator<=(poly_ptr const&, poly_ptr const&) noexcept¶
- bool operator>(poly_ptr const&, poly_ptr const&) noexcept¶
- bool operator<(poly_ptr const&, poly_ptr const&) noexcept¶
Compares two poly_ptr‘s via poly_ptr<T, Deleter>::get() with the given operator.
- bool operator==(deep_ptr const&, deep_ptr const&) noexcept
- bool operator!=(deep_ptr const&, deep_ptr const&) noexcept
- bool operator>=(deep_ptr const&, deep_ptr const&) noexcept
- bool operator<=(deep_ptr const&, deep_ptr const&) noexcept
- bool operator>(deep_ptr const&, deep_ptr const&) noexcept
- bool operator<(deep_ptr const&, deep_ptr const&) noexcept
Compares two deep_ptr‘s via deep_ptr<T, Deleter>::get() with the given operator.
- bool operator==(poly_ptr<T, D> const&, nullptr_t) noexcept
- bool operator!=(poly_ptr<T, D> const&, nullptr_t) noexcept
- bool operator>=(poly_ptr<T, D> const&, nullptr_t) noexcept
- bool operator<=(poly_ptr<T, D> const&, nullptr_t) noexcept
- bool operator>(poly_ptr<T, D> const&, nullptr_t) noexcept
- bool operator<(poly_ptr<T, D> const&, nullptr_t) noexcept
- bool operator==(nullptr_t, poly_ptr<T, D> const&) noexcept
- bool operator!=(nullptr_t, poly_ptr<T, D> const&) noexcept
- bool operator>=(nullptr_t, poly_ptr<T, D> const&) noexcept
- bool operator<=(nullptr_t, poly_ptr<T, D> const&) noexcept
- bool operator>(nullptr_t, poly_ptr<T, D> const&) noexcept
- bool operator<(nullptr_t, poly_ptr<T, D> const&) noexcept
Returns: the result of comparing poly_ptr<T, Deleter>::get() and nullptr with the given operator.
- bool operator==(deep_ptr<T, D, C> const&, nullptr_t) noexcept
- bool operator!=(deep_ptr<T, D, C> const&, nullptr_t) noexcept
- bool operator>=(deep_ptr<T, D, C> const&, nullptr_t) noexcept
- bool operator<=(deep_ptr<T, D, C> const&, nullptr_t) noexcept
- bool operator>(deep_ptr<T, D, C> const&, nullptr_t) noexcept
- bool operator<(deep_ptr<T, D, C> const&, nullptr_t) noexcept
- bool operator==(nullptr_t, deep_ptr<T, D, C> const&) noexcept
- bool operator!=(nullptr_t, deep_ptr<T, D, C> const&) noexcept
- bool operator>=(nullptr_t, deep_ptr<T, D, C> const&) noexcept
- bool operator<=(nullptr_t, deep_ptr<T, D, C> const&) noexcept
- bool operator>(nullptr_t, deep_ptr<T, D, C> const&) noexcept
- bool operator<(nullptr_t, deep_ptr<T, D, C> const&) noexcept
Returns: The result of comparing deep_ptr<T, Deleter, Copier>::get() and nullptr with the given operator.
- bool operator==(observer_ptr const&, observer_ptr const&)
- bool operator!=(observer_ptr const&, observer_ptr const&)
- bool operator>=(observer_ptr const&, observer_ptr const&)
- bool operator<=(observer_ptr const&, observer_ptr const&)
- bool operator>(observer_ptr const&, observer_ptr const&)
- bool operator<(observer_ptr const&, observer_ptr const&)
Returns: The result of comparing the objects watched by observer_ptr via the given operator.
- bool operator==(observer_ptr const&, std::nullptr_t)
- bool operator!=(observer_ptr const&, std::nullptr_t)
- bool operator==(std::nullptr_t, observer_const&)
- bool operator!=(std::nullptr_t, observer_const&)
Returns: The result of comparing the objects watched by observer_ptr with nullptr via the given operator
Make Functions¶
- observer_ptr<T> make_observer(W* ptr)¶
- observer_ptr<T> make_observer(std::unique_ptr<W, D> const& ptr)¶
- observer_ptr<T> make_observer(std::weak_ptr<W> const& ptr)¶
- observer_ptr<T> make_observer(deep_ptr<W, C, D> const& ptr)¶
- observer_ptr<T> make_observer(poly_ptr<W, D> const& ptr)¶
Provided to supplement the other make_* functions for smart pointers, the make_observer function will create an observer from any C++11 standard smart pointer, a raw pointer, or the smart pointers provided by MNMLSTC Core
- poly_ptr<T, Deleter> make_poly<T>(U&& args)¶
Provided to supplement the std::make_shared<T> and make_unique<T>() functions. Constructs a poly_ptr with an element_type of T, taking derived universal reference U. This function internally calls make_unique<T>() to create the poly_ptr.
- deep_ptr<T> make_deep<T>(args)¶
Used to supplement the make_unique<T>(), make_poly<T>(), and make_shared<T> functions. Takes a variadic number of arguments to construct a T with. This T is allocated via operator new (the default allocation scheme) and passed to a deep_ptr for construction. This deep_ptr is then returned by the function.
- std::unique_ptr<T> make_unique<T>(args)¶
- std::unique_ptr<T> make_unique<T>(size)
make_unique is provided to help supplement the std::make_shared<T> function for the std::unique_ptr<T> type. The first overload will be used if the given type T is not an array. If the given type T is an array of an unknown bound (that is, std::extent<T>::value == 0) the second overload is used. A third overload is provided to insure that the compiler
will error. This third overload is available when the given type T is an array of a known bound (that is, std::extent<T>::value != 0).
Parameters: - args – Variadic template arguments with which to construct a T
- size – Extent of std::unique_ptr<T[]> desired.
Type args: Args&&...
Returns: std::unique_ptr<T>
Type size: std::size_t
Returns: std::unique_ptr<T[]>
Specializations¶
There are specializations for poly_ptr and deep_ptr for integration with the C++ standard library.
- class hash<poly_ptr<T, Deleter>>¶
This specialization of hash allows poly_ptr to be used as a key type in associative containers.
For a given poly_ptr ptr, this specialization insures that std::hash<poly_ptr<T, Deleter>> { }(ptr) is equivalent to the expression std::hash<typename poly_ptr<T, Deleter>::pointer> { }(ptr.get())
- class std::hash<deep_ptr<T, Deleter, Copier>>¶
This specialization of hash allows deep_ptr to be used as a key type in associative containers.
For a given deep_ptr ptr, this specialization insure that std::hash<deep_ptr<T, Deleter, Copier>> { }(ptr) is equivalent to the expression std::hash<typename deep_ptr<T, Deleter, Copier>::pointer> { }(ptr.get())
- void swap(poly_ptr<T, D>& lhs, poly_ptr<T, D>& rhs) noexcept¶
A specialization of std::swap that calls poly_ptr<T, Deleter>::swap().
- void swap(deep_ptr<T, D, C>& lhs, deep_ptr<T, D, C>& rhs) noexcept¶
A specialization of std::swap that calls deep_ptr<T, Deleter, Copier>::swap().