I am working with a fortran code which contains some array pointers; depending on the user input, they might either be set to point to another array with the assignment statement =>
, or they might be directly allocated with the allocate
statement.
This creates a problem for freeing memory at the end of the code, because in the first case I just want to nullify
the pointer, while in the second case I need to deallocate
it to prevent a memory leak. The problem is that in both cases associated
returns true, so I don't know how to tell which case I'm in without keeping track of it manually. Since there are many such array pointers I would prefer to avoid having to do so.
Is there a simple way to distinguish between the two cases?
Thank you!
This sounds like a horrible mess. I assume that you have something like this:
program really_bad
implicit none
integer, dimension(:), pointer :: a
integer, dimension(:), allocatable, target :: b
logical :: point_to_b
allocate(b(10))
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if (point_to_b) then
a => b
else
allocate(a(10))
end if
! Destroy A
end program really_bad
And your issue is the destroying part. If a
is pointing to b
, then you want to NULLIFY
it, because you need to keep b
. But if a
is not pointing to b
, and you only NULLIFY
it, you get a memory leak.
As @ian-bush points out, you can check whether something is associated with something else, but that would mean that you have to compare the pointer with all possible targets, and you claim that you have many of those.
One thing you could try, but this is probably even worse an idea, is to try to deallocate, and use its STAT=
dummy argument to check whether the deallocation actually worked. Please note that this is a terrible hack, and I only got it to work on Intel, not on GFortran, but here goes nothing:
program don
implicit none
integer, dimension(:), pointer :: a
integer, dimension(:), target, allocatable :: b
logical :: point_to_b
integer :: stat
allocate(b(10))
b = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if ( point_to_b ) then
a => b
else
allocate(a(10))
end if
deallocate(a, stat=stat)
if ( stat /= 0 ) nullify(a)
end program don
A far better method would probably be to replace your array pointers with types that have both the pointer and a flag how it was associated.
program don
implicit none
type :: my_array_pointer_type
integer, dimension(:), pointer :: array
logical :: master
end type my_array_pointer_type
type(my_array_pointer_type) :: a
integer, dimension(:), target, allocatable :: b
logical :: point_to_b
integer :: stat
allocate(b(10))
b = (/ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /)
write(*, *) "Should I point to B?"
read(*, *) point_to_b
if ( point_to_b ) then
a%master = .false.
a%array => b
else
a%master = .true.
allocate(a%array(10))
end if
if (a%master) then
deallocate(a%array)
else
nullify(a%array)
end if
end program don
Neither of these suggestions will help you if you might then point another pointer at a
and then try to destroy it. In that case I really suggest to rethink your whole project design.
How complicated is your data structure? That is, how many pointers are simultaneously associated to a single target at a given time? The Fortran standard is completely silent when it comes to reference counting a target instantiated through the allocate
statement, i.e., it doesn't specify a mechanism to ensure that the target memory is released when all of it's pointees are disassociated. Built-in garbage collection is precisely why the allocatable
attribute is prefered over the pointer
attribute 99.9% of the time. In the rare event that you absolutely must use a pointer and its target is allocated in some other subprogram, then passed up the call chain to a different procedure, tracking pointer ownership becomes a nightmare.
If you have access to a modern Fortran compiler (2008+), the following module might be helpful; FYI, variables declared in a module automatically inherit the save
attribute. The target foo
is a private module variable and ref_counter
keeps track of the number of pointers associated with foo
. By overloading the deallocate
statement we can safely nullify, or nullify + deallocate. with call deallocate(some_ptr, dealloc_stat)
module foo_target
! Explicit typing only
implicit none
private
public :: deallocate, nullify, point_at_foo
! Neither foo nor ref_counter are public
real, pointer :: foo(:) => null()
integer, protected :: ref_counter = 0
interface deallocate
module procedure deallocate_ptr
end interface deallocate
interface nullify
module procedure nullify_ptr
end interface nullify
contains
subroutine deallocate_ptr(ptr_to_foo, return_stat)
! Dummy arguments
real, pointer, intent (in out) :: ptr_to_foo(:)
integer, intent (out) :: return_stat
! Local variables
enum, bind(C)
enumerator :: DEALLOC_ERROR=1, NO_ASSOC, BAD_ASSOC
end enum
integer :: alloc_stat
! Initialize
return_stat = 0
block_construct: block
! Check association
if (ref_counter == 0 .or. .not. associated(foo)) then
return_stat = NO_ASSOC
exit block_construct
else if (.not. associated(ptr_to_foo, foo)) then
return_stat = BAD_ASSOC
exit block_construct
end if
if (ref_counter > 1 ) then
! Further associations still persist, only nullify
call nullify(ptr_to_foo)
else if (ref_counter == 1) then
! No remaining associations, nullify and deallocate
call nullify(ptr_to_foo)
deallocate(foo, stat=alloc_stat)
! Check deallocation status
if (alloc_stat /= 0) return_stat = DEALLOC_ERROR
end if
end block block_construct
end subroutine deallocate_ptr
subroutine nullify_ptr(ptr_to_foo)
! Dummy arguments
real, pointer, intent (in out) :: ptr_to_foo(:)
! Terminate association
nullify(ptr_to_foo)
! Decrement
ref_counter = ref_counter - 1
end subroutine nullify_ptr
function point_at_foo() result (return_value)
! Dummy arguments
real, pointer :: return_value(:)
! Establish association
return_value => foo
! Increment
ref_counter = ref_counter + 1
end function point_at_foo
end module foo_target
Maybe it will be be useful to someone. I pushed to GitHub my simple implementation of reference counting which enables you to track references to variables. When number of references is 0 the object is automatically deallocated. Link https://github.com/LadaF/Fortran-RefCount
It is based on ideas from paper Car - A reference counting implementation in Fortran 95/2003 and book Scientific Software Design: The Object-Oriented Way.
The reference object itself stores a pointer to another object refptr
which stores the pointer to the data itself and the number of references pointing to it. Assignments are overloaded so that the reference count is updated. Finalization of reference objects subtracts from the reference counts as required.
type refptr
integer :: ref_count = 0
class(*), allocatable :: data
contains
final :: refptr_finalize
end type
type :: ref
type(refptr),pointer :: ptr => null()
integer :: weak = 0 !1.. this is a weak reference, -1.. make weak references to this
contains
procedure :: assign_star
generic :: assignment(=) => assign_star
...
procedure :: pointer => ref_pointer
final :: ref_finalize
end type
interface ref
module procedure ref_init
module procedure ref_init_1
end interface
Usage is then very simple:
type(ref) :: a, b
a = 42
print *, "a:", a%value(0)
!this does not copy the value, it points the reference
b = a
print *, "b:", b%value(0)
!sets a to point to something new, b still points to 42
a = 2.3
Disclaimer: The automatic finalization has just been implemented and not tested too much. I have so far used it with manual calls to finalize in gfortran-4.8. This has been tested quite extensively.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With