-
Notifications
You must be signed in to change notification settings - Fork 13
ftlList
Lists are sequence containers that allow constant time insert and erase operations anywhere within the sequence, and iteration in both directions.
The ftlList is implemented as doubly-linked list. Doubly linked lists store each of the elements they contain in different and unrelated storage locations. The ordering is kept internally by the association to each element of a link to the element preceding it and a link to the element following it.
Compared to other base standard sequence containers (ftlDynArray, ftlDeque), lists perform generally better in inserting, extracting and moving elements in any position within the container for which an iterator has already been obtained.
The main drawback of lists compared to these other sequence containers is that they lack direct access to the elements by their position. For example, to access the sixth element in a list, one has to iterate from a known position (like the beginning or the end) to that position, which takes linear time in the distance between these. They also consume some extra memory to keep the linking information associated to each element (which may be an important factor for large lists of small-sized elements).
ftlList is basically a Fortran reimplementation of C++'s `std::list`_.
ftlList uses the following macros for instantiation:
FTL_TEMPLATE_TYPE
- The type of the objects to be stored in the container, e.g.
integer(int64)
orMyDerivedType
. Do not add the enclosingtype()
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, or uses a kind defined in another module, e.g.int64
from theiso_fortran_env
module. FTL_TEMPLATE_TYPE_NAME
- A convenient user-readable name for the stored type, e.g.
Int
. This will be used in the type-name of the list container itself and the module in which it is available, e.g. setting this toInt
will result in the typeftlListInt
available in moduleftlListIntModule
. FTL_INSTANTIATE_TEMPLATE
- If this macro is defined the template will be instantiated. If not, the template will not be instantiated, but some information about the ftlList container (e.g. its name and the kind of iterator it provides) will be passed on to subsequent template instantiations (e.g. ftlAlgorithms).
FTL_TEMPLATE_TYPE_PROVIDES_FTLMOVE
- This macro should be defined if the template type implements the
ftlMove
method. This is useful for large types containing arrays or owning pointers so that copying would be expensive. The ftlList container will then use theftlMove
method of the template type to move elements around instead of doing deep copies via (user defined) assignment. Currently this is only used to move the instance of the type out of the list into the return values of thePopBack
orPopFront
methods.
Example instantiation of a ftlList for integer(int64)
. The resulting type
ftlListInt
is available in ftlListIntModule
.
#define FTL_TEMPLATE_TYPE integer(int64)
#define FTL_TEMPLATE_TYPE_NAME Int
#define FTL_TEMPLATE_TYPE_MODULE iso_fortran_env
#define FTL_INSTANTIATE_TEMPLATE
#include "ftlList.F90_template"
Example instantiation of a ftnList for a derived type (Point2D
) defined in
module Point2DModule
. The resulting type ftlListPoint2D
is available in
ftlListPoint2DModule
.
#define FTL_TEMPLATE_TYPE Point2D
#define FTL_TEMPLATE_TYPE_IS_DERIVED
#define FTL_TEMPLATE_TYPE_MODULE Point2DModule
#define FTL_TEMPLATE_TYPE_NAME Point2D
#define FTL_INSTANTIATE_TEMPLATE
#include "ftlList.F90_template"
Constructs a new ftlList container from a variety of data sources:
Default constructor. Constructs an empty container.
subroutine New(self) type(ftlListT), intent(out) :: selfExample usage:
type(ftlListInt) :: lst call lst%New() ! empty and size(lst) == 0Constructs the container with a size of n, optionally populating it with
n
copies ofval
.subroutine New(self, n, val) type(ftlListT), intent(out) :: self integer , intent(in) :: n T , intent(in) , optional :: valExample usage:
type(ftlListInt) :: lst call lst%New(5) ! list with 5 uninitialized integers call lst%New(10, 0) ! with 10 integers of value 0Copy constructor. Constructs the container with the copy of the contents of
other
.subroutine New(self, other) type(ftlListT), intent(out) :: self type(ftlListT), intent(in) :: otherExample usage:
type(ftlListInt) :: lst1, lst2 call lst1%New(5, 1) ! list with 5 integers of value 1 call lst2%New(lst1) ! copy of lst2Construction from a normal Fortran array.
subroutine New(self, array) type(ftlListT), intent(out) :: self T , intent(in) :: array(:)Example usage:
type(ftlListInt) :: lst call lst%New([1,5,8,0]) ! size is now 4, values are as specified
Destructs the container. The used storage is deallocated. Note that it is not necessary to call the destructor explicitly. Memory will also be deallocated automatically whenever a list goes out of scope.
subroutine Delete(self) type(ftlListT), intent(inout) :: selfExample usage:
type(ftlListInt) :: lst call lst%New([1,2,3,4,5,6,7,8]) call lst%Delete() ! deallocates all memory owned by lst
Replaces the contents of the container, modifying its size accordingly. For ftlList, assignment is absolutely equivalent to using the constructors directly. (This is not true for all FTL containers, see ftlDynArray)
Copy assignment. Replaces the contents with a copy of the contents of other.
subroutine assignment(=)(self, other) type(ftlListT), intent(inout) :: self type(ftlListT), intent(in) :: otherArray assignment. Replaces the contents with a copy of a Fortran array.
subroutine assignment(=)(self, array) type(ftlListT), intent(inout) :: self T , intent(in) :: array(:)Example usage:
type(ftlListInt) :: lst1, lst2 lst1 = [1,2,3,4] ! assignment also works on uninitialized lists lst2 = [5,6,7,8,9,0] lst2 = lst1 ! is now [1,2,3,4] but without reallocationNote that unlike C++'s `std::list`_, the ftlList currently does not have the assignment method versions that take the number of arguments or iterator pairs. Just use the
ftlList%New()
methods instead.
Returns a bidirectional iterator to the first element of the container.
ftlListTIterator function Begin(self) type(ftlListT), intent(in) :: selfIf the container is empty, the returned iterator will be equal to
End()
. Note that this method exists as both type-bound and as a free function.
Returns a bidirectional iterator to the element following the last element of the container. This element acts as a placeholder; attempting to access it results in undefined behavior.
ftlListTIterator function End(self) type(ftlListT), intent(in) :: selfNote that this method exists as both type-bound and as a free function.
Provides direct access to first/last element stored in the container.
Example usage:
type(ftlListInt) :: lst lst = [1,2,3,4] ! assignment also works on uninitialized dynArrays write (*,*) lst%front, lst%back ! prints 1 4
Checks if the container has no elements (i.e. its size is 0). This function does not modify the container in any way. See
ftlList%Clear()
for a method that deletes the container's contents.pure logical function Empty(self) type(ftlListT), intent(in) :: self
Returns the number of elements in the container. Note that this method exists as a type-bound procedure as well as a free function which mimics the Fortran
size()
intrinsic for arrays.pure integer function Size(self) type(ftlListT), intent(in) :: selfExample usage:
type(ftlListInt) :: lst call lst%New([1,2,3,4,5,6,7]) call lst%PushBack(42) write (*,*) lst%Size(), size(lst) ! prints: 8 8
Removes all elements from the container. Invalidates any pointers, or iterators referring to contained elements. The past-the-end iterator remains valid
subroutine Clear(self) type(ftlListT), intent(inout) :: self
Resizes the container to contain
n
elements. If the current size is greater thann
, the container is reduced to its firstn
elements. If the current size is less thann
, additional elements are appended and optionally initialized with copies ofval
.subroutine Resize(self, n, val) type(ftlListT), intent(inout) :: self integer , intent(in) :: n T , intent(in), optional :: val
Appends an element to the end of the list. The new element is initialized as a copy of value.
subroutine PushBack(self, val) type(ftlListT), intent(inout) :: self T , intent(in) :: valNote that the past-the-end iterator remains valid.
Returns and removes the last element of the container.
T function PopBack(self) type(ftlListT), intent(inout) :: selfThe iterator to the removed element is invalidated, all other iterators stay valid.
Calling this method on an empty container is undefined behavior.
Inserts an element at the beginning of the list. The new element is initialized as a copy of value.
subroutine PushFront(self, val) type(ftlListT), intent(inout) :: self T , intent(in) :: val
Returns and removes the last element of the container.
T function PopFront(self) type(ftlListT), intent(inout) :: selfThe iterator to the removed element is invalidated, all other iterators stay valid.
Calling this method on an empty container is undefined behavior.
Inserts elements at the specified location in the container. All iterators remain valid.
There are several versions of the insertion method:
Insert a copy of
val
before iteratorit
.subroutine Insert(self, it, val) type(ftlListT) , intent(inout) :: self type(ftlListTIterator), intent(in) :: it T , intent(in) :: valInsert
n
copies ofval
before iteratorit
.subroutine Insert(self, it, n, val) type(ftlListT) , intent(inout) :: self type(ftlListTIterator), intent(in) :: it integer , intent(in) :: n T , intent(in) :: valInsert a copy of
array
before iteratorit
.subroutine Insert(self, it, array) type(ftlListT) , intent(inout) :: self type(ftlListTIterator), intent(in) :: it T , intent(in) :: array(:)Insert a copy of the elements in the range [
first,``last
) before iteratorit
.subroutine Insert(self, it, first, last) type(ftlListT) , intent(inout) :: self type(ftlListTIterator), intent(in) :: it type(ftlListTIterator), intent(in) :: first, last
Removes specified elements from the container. Invalidates iterators to the removed elements.
Note that in contrast to the erase function of C++'s `std::list`_ (which returns an iterator to the element following the last removed) this method is a subroutine and does not return anything. This was done because on Fortran it is not possible to ignore the return value of a function.
TODO: Implement an alternative erase method which corresponds to the erase function of C++'s `std::list`_.
There are two versions of the erase method:
Erase the element referenced by iterator
it
.subroutine Erase(self, pos) type(ftlListT) , intent(inout) :: self type(ftlListTIterator), intent(in) :: itErase the elements in the iterator range [
first
,last
).subroutine Erase(self, first, last) type(ftlListT) , intent(inout) :: self type(ftlListTIterator), intent(in) :: first, last
Swaps the contents of two lists.
subroutine ftlSwap(lst1, lst2) type(ftlListT), intent(inout) :: lst1, lst2The same effect could also be achieved with assignment (or copy construction):
type(ftlListInt) :: lst1, lst2, tmp tmp = lst2 lst2 = lst1 lst1 = tmp ! ^-- same result as ftlSwap(lst1, lst2)Note however, that this way of swapping is an O(N) operation, while ftlSwap is O(1) with a really small prefactor (it only swaps some pointers). Use ftlSwap whenever you want to exchange two lists.
Moves the contents of one list to another list.
subroutine ftlMove(src, dest) type(ftlListT), intent(inout) :: src type(ftlListT), intent(out) :: destThis is an O(1) operation. The src list is empty after the moving.
Note that it is not necessary for the template type to implement
ftlMove
in order for the ftlList to be movable. The ftlList is always movable, no matter if the template type is movable.