ooppointersconstructorfortranfortran2008

Fortran constructor returning pointer to allocated object


In this question: Fortran Functions with a pointer result in a normal assignment, it is stated that functions returning pointers are not recommended.

My question concerns constructors of user defined types. Consider the code below:

program PointTest

   use PointMod, only: PointType

   implicit none

   class(PointType), allocatable :: TypeObject

   TypeObject = PointType(10)

end program PointTest
module PointMod

   implicit none

   type PointType

      real(8), dimension(:), allocatable :: array

   contains 

      final :: Finalizer

   end type PointType

   interface PointType

      procedure NewPointType

   end interface PointType


contains


   function NewPointType(n) result(TypePointer)

      implicit none

      integer, intent(in) :: n

      type(PointType), pointer :: TypePointer

      allocate(TypePointer)

      allocate(TypePointer%array(n))

   end function NewPointType


   subroutine Finalizer(this)

      implicit none

      type(PointType) :: this

      print *, 'Finalizer called'

   end subroutine Finalizer


end module PointMod

In the code, I have defined a type with a constructor that allocates the object and then allocates an array in the object. It then returns a pointer to the object.

If the constructor just returned the object, the object and the array would be copied and then deallocated (at least with standard compliant compilers). This could cause overhead and mess with our memory tracking.

Compiling the above code with ifort gives no warnings with -warn all (except unused variable in the finalizer) and the code behaves the way I expect. It also works fine with gfortran, except I get a warning when using -Wall

    TypeObject = PointType(10)
                1
Warning: POINTER-valued function appears on right-hand side of assignment at (1) [-Wsurprising]

What are the risks of using constructors like these? As far as I can tell, there will be no dangling pointers and we will have more control on when objects are allocated. One workaround that would achieve the same result is to explicitly allocate the object and turn the constructor into a subroutine that sets variables and does the allocation of array, but it looks a lot less elegant. Are there other solutions? Our code is in the Fortran 2008 standard.


Solution

  • Do not use pointer valued functions. As a rule I never make functions that return functions. They are bad and confusing. They lead to nasty bugs, especially when one confuses => and =.

    What the function does is that it allocates a new object and creates a pointer that allocates the object.

    What

    TypeObject = PointType(10)
    

    does is that it copies the value of the object stored in the pointer. Then the pointer is forgotten and the memory where the pointer had pointed is leaked and lost forever.


    You write "As far as I can tell, there will be no dangling pointers and we will have more control on when objects are allocated." However, I do not see a way to avoid the dangling pointer allocated inside the function. Not even a finalizer can help here. I also do not see how you have more control. The memory you explicitly allocated is just lost. You have a different memory for TypeObject (likely on the main program's stack) and the array inside the type will get allocated again during the copy at the intrinsic assignment TypeObject = PointType(10).

    The finalizer could take care of the array component so the array allocated inside the function does not have to be lost. However, the type itself, to which the pointer TypePointer points, with its non-allocatable non-pointer components and descriptors and so on, cannot be deallocated from the finalizer and will remain dangling and the memory will be leaked.


    Do not be afraid of functions that return objects as values. That is not a problem. Compilers are smart and are able to optimize an unnecessary copy. Compiler might be easily able to find out that you are just assigning the function result so it can use the memory location of the assignment target for the function result variable (if it does not have to be allocatable).

    Many other optimizations exist.

       function NewPointType(n) result(TypePointer)   
          integer, intent(in) :: n
    
          type(PointType) :: TypePointer
    
          allocate(TypePointer%array(n))    
       end function NewPointType
    

    is simpler and should work just fine. With optimizations it could even be faster. If using a non-pointer non-allocatable result is not possible, use allocatable. Do not use pointers for function results.