ooppointersfortranfortran2003

Copying pointer association in a Fortran derived type structure


I am trying to copy the target to a pointer in a derived type variable to another variable of the same type, but I have no idea how to do it

Say that I have a scattered list of points that I want to triangulate. I will have a

type :: triangulation
   type(vertex), allocatable :: points(:)
   type(triangle), allocatable :: triangles(:)
end type triangulation

To save memory, I am defining each triangle using pointers to the nodes:

type :: triangle
   type(vertex), pointer :: A => null()
   type(vertex), pointer :: B => null()
   type(vertex), pointer :: C => null()
end type triangle

that will be in the same object. For example, to generate a new triangle in the triangulation, I will run:

type(triangle) function triangulation_new_triangle(this,iA,iB,iC) result(tri)
   class(triangulation), intent(in), target :: this
   integer, intent(in) :: iA, iB, iC ! Indices of the three nodes

   tri%A => this%points(iA)
   tri%B => this%points(iB)
   tri%C => this%points(iC)

end function 

Now, assume I have to reallocate the list of triangles. How do I copy it to a new list for the same triangulation, without losing their pointer references? I.e., I wanna do something like this:

subroutine triangulation_reallocate(this)
   class(triangulation), intent(inout) :: this

   ! Local variables
   integer, parameter :: TRIANGLE_CHUNK_SIZE = 1024
   integer :: old_size
   type(triangle), allocatable :: new_triangle_pool(:)

   ! Get old size
   old_size = merge(size(this%triangles),0,allocated(this%triangles))

   ! Allocate new array of triangles
   allocate(new_triangle_pool(old_size+TRIANGLE_CHUNK_SIZE))

   ! Copy data from old to new array
   [..... missing code here .......]

   ! Move allocation back to the triangulation object
   call move_alloc(from=new_triangle_pool,to=this%triangles)

end subroutine triangulation_reallocate

I am thinking of a way to copy the triangle associations. If I do something like:

do i=1,old_size
   new_triangle_pool(i) = this%triangles(i)
end do

will that work? I am concerned that if I copy something like:

new_triangle_pool(i)%A => this%triangles(i)%A

then this is a pointer-to-a-pointer and not to the original vertex, and the association will be lost when the old this%triangles variables is deallocated?


Solution

  • Your suggestion should work. However, a simpler suggestion:

    new_triangle_pool = this%triangles
    

    Since new_triangle_pool is ALLOCATABLE, the array should automatically be allocated to the size of the source of the assignment (this%triangles), then be initialized via an element-wise copy from the source.

    Pointer components of a derived type are copied as a shallow-copy by default. This means that no memory is duplicated, but rather the A, B, and C pointers on each element of new_triangle_pool will become associated with the same target as the A, B, and C pointers on the corresponding element of this%triangles. If the pointers on an element of this%triangles become associated with a different target or nullified, the pointers on new_triangle_pool will remain associated with the original target.

    The only way you would run into an issue is if the pointers on this%triangles were to be deallocated instead of nullified. At that point, the memory would no longer be allocated, and you would be left with dangling pointers on new_triangle_pool. However, with some care you can avoid that problem.

    If a deep copy is needed (i.e., allocate the pointers in new_triangle_pool and then copy the values from this%triangles into the new memory), then you must explicitly write code to do so. Otherwise, only a shallow copy will be done by default (which I believe is what you want).