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.
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.