-
Notifications
You must be signed in to change notification settings - Fork 13
ftlSharedPtr
ftlSharedPtr is a smart pointer that retains shared ownership of an object through a pointer. Several ftlSharedPtr objects may own the same object. The object is destroyed and its memory deallocated when either of the following happens:
- the last remaining ftlSharedPtr owning the object goes out of scope
- the last remaining ftlSharedPtr owning the object is assigned another pointer via assignment(=) or is nullified.
The object is destroyed through deallocation and optionally through calling custom destructor method on it.
The object whose lifetime is managed by the ftlSharedPtr is accessible through
the value
data member.
subroutine DoesNotLeak
integer, pointer :: i
type(ftlSmartPtrInt) :: p1, p2
allocate(i)
i = 42
call p1%AssumeOwnershipOf(i)
write (*,*) p1%UseCount(), p1%Unique() ! prints: 1 True
p2 = p1 ! p2 and p1 now share ownership of i
write (*,*) p1%UseCount(), p1%Unique() ! prints: 2 False
call p1%Nullify() ! p2 is now the only owner
write (*,*) p2%UseCount(), p2%Unique() ! prints: 1 True
write (*,*) p2%value ! prints 42
! i will be deallocated by the finalizer of p2 at the end of the
! subroutine ...
end subroutine
ftlSharedPtr is basically a reimplementation of C++'s std::shared_ptr, though the interface is a bit more explicit.
ftlSharedPtr uses the following macros for instantiation:
FTL_TEMPLATE_TYPE
- The type of the objects to be managed by the shared pointer. Do not add the
enclosing
type()
for derived types. FTL_TEMPLATE_TYPE_IS_DERIVED
- This needs to be defined if
FTL_TEMPLATE_TYPE
is a derived type. FTL_TEMPLATE_TYPE_MODULE
- The name of the module in which
FTL_TEMPLATE_TYPE
is defined. Specifying this is probably only necessary ifFTL_TEMPLATE_TYPE
is a derived type. FTL_TEMPLATE_TYPE_NAME
- A convenient user-readable name for the managed type, e.g.
Int
. This will be used in the type-name of the ftlSharedPtr itself and the module in which it is available, e.g. setting this toInt
will result in the typeftlSharedPtrInt
available in moduleftlSharedPtrIntModule
. FTL_INSTANTIATE_TEMPLATE
- If this macro is defined the template will be instantiated. If not, the template will not be instantiated.
TODO: example usage for all methods
Raw Fortran pointer associated with the managed object. Should be used to access the pointed to object, like one would use the dereference operator on C++'s std::shared_ptr.
While it is technically possible to manually reassociate this pointer with another object (aka
ftlSharedPtr%value => someOtherObject
), this should never be done. The originally pointed to object will not be cleaned up properly! This caveat could be avoided by makingvalue
private and providing a getter function, but this would prevent direct access to members of the managed object (akaftlSharedPtr%value%someMember
), which would be very inconvenient. So never reassociateftlSharedPtr%value
directly. Use it like you would dereference a pointer in C/C++ ...
Constructs a shared pointer to a newly allocated object.
subroutine Allocate(self) type(ftlSharedPtrT), intent(out) :: self
Constructs a shared pointer that assumes the ownership of an object pointed to by a (owning!) raw Fortran pointer.
subroutine AssumeOwnershipOf(self, p) type(ftlSharedPtrT), intent(out) :: self T, pointer , intent(in) :: pAfter construction the lifetime of the object pointed by
p
is managed by the shared pointer. The raw pointer should therefore not be deallocated manually. It will be deallocated when the last shared pointer to it goes out of scope, so deallocating it manually would result in a double deallocation.
Assignment from another ftlSharedPtr is used to share ownership of an object:
subroutine assignment(=)(lhs, rhs) type(ftlSharedPtrT), intent(out) :: self type(ftlSharedPtrT), intent(in) :: otherIf
rhs
is associated the ownership of the pointed object is shared by both ftlSharedPtrs. The last one to go out of scope takes care of deleting it. Ifrhs
is not associated,lhs
is also not associated after the assignment. (Note that the two shared pointers are independently unassociated. They will not automagically become associated to the same object if only one of them is associated with it.)
subroutine Delete(self) type(ftlSharedPtrT), intent(inout) :: selfNullifies an instance of a shared pointer (causing it to become unassociated), but before has the following side effects:
- If the
UseCount
of the shared pointer is greater than one (meaning there are other shared pointers pointing to the same object), it will be decreased by one.- If the
UseCount
of the shared pointer is one (meaning it is the only shared pointer currently pointing to the object), the object will be deallocated (and its finalizer will be called).- If the shared pointer is already unassociated, nothing happens.
Note that it is not necessary to call
Nullify()
on a ftlSharedPtr manually. The method is called automatically as the finalizer of ftlSharedPtr, though it might be useful to call it manually in order to release resources early.
Checks the association status of a shared pointer, similar to how the
associated()
Fortran intrinsic works for raw pointers.Just like for the Fortran intrinsic, there are three different versions.
Check if a shared pointer is associated to anything at all.
pure logical function Associated(self) type(ftlSharedPtrT), intent(in) :: selfNote that all ftlSharedPtrs start out being unassociated. This is different from normal Fortran pointers which (for some reason) start out being undefined unless they are initialized with
=> null()
in the declaration. This is not necessary for ftlSharedPtr. If you don't associate your shared pointer with anything, it's unassociated. Period.Check if two shared pointers are associated with the same target.
pure logical function Associated(self, other) type(ftlSharedPtrT), intent(in) :: self type(ftlSharedPtrT), intent(in) :: otherCheck if a shared pointer is associated with the same target as a raw pointer. (This is the case, when the shared pointer is managing the lifetime of the object pointed to by the raw pointer.)
pure logical function Associated(self, p) type(ftlSharedPtrT), intent(in) :: self T, pointer , intent(in) :: p
Returns the number of shared pointers currently sharing the ownership of the associated object.
pure integer function UseCount(self) type(ftlSharedPtrT), intent(in) :: self
Return whether or not the shared pointer is the only shared pointer currently associated with the object. This is equivalent to checking whether or not
UseCount() == 1
.pure logical function Unique(self) type(ftlSharedPtrT), intent(in) :: selfNote that unassociated shared pointers are not considered unique.
Swaps two shared pointers, transferring ownership of any managed object between them without destroying or altering the
UseCount
of either.subroutine Swap(self, other) type(ftlSharedPtrT), intent(inout) :: self type(ftlSharedPtrT), intent(inout) :: otherThe same effect could also be achieved with assignment and a temporary shared pointer:
type(ftlSharedPtrInt) :: sp1, sp2, tmp tmp = sp2 sp2 = sp1 sp1 = tmp ! ^-- same result as ftlSwap(sp1, sp2)Using
ftlSwap()
is more convenient though, and (slightly) faster.
Transfers the ownership of an object from one shared pointer to another. The useCount is unchanged and the source shared pointer becomes unassociated in the process.
subroutine ftlMove(src, dest) type(ftlSharedPtrT), intent(inout) :: src type(ftlSharedPtrT), intent(out) :: dest