Reputation: 1662
I am trying to decipher a fortran code. It passes a pointer to a function as an actual argument, and the formal argument is instead a target. It defines and allocates a pointer of type globalDATA in the main program, then it calls a function passing that pointer:
module dataGLOBAL
type globalDATA
type (gl_1) , pointer :: gl1
type (gd_2) , pointer :: gd2
type (gdt_ok) , pointer :: gdtok
...
...
end type globalDATA
end module dataGLOBAL
Program main
....
....
use dataGLOBAL
...
type(globalDATA),pointer :: GD
allocate(GD)
returnvalue = INIT(GD)
....
....
end
The function reads:
integer function INIT(GD) result(returnvalue)
....
....
use dataGLOBAL
type(globalDATA) , target :: GD
allocate (GD%gl1)
allocate (GD%gd2)
allocate (GD%gdtok)
....
....
end function INIT
What is the meaning of doing this? And why do both the pointer in the main program and the single components of the target structure have to be allocated? thanks A.
Upvotes: 4
Views: 4927
Reputation: 3812
Because the pointer attribute is not specified for the dummy argument, the entire derived type GD
is passed from the main code (not the pointer to it). On the subroutine side, you could explicitely write
integer function INIT(GD) result(returnvalue)
...
use dataGLOBAL
type(globalDATA), intent(inout), target :: GD
to make it more clear. The target attribute of the dummy argument only ensures, that you can point to that argument inside the subroutine via pointer assignment.
As long as you are only manipulating the fields of the derived type, but not the derived type as whole (e.g. by allocating or deallocating it), it should not make a difference, whether you call the INIT
routine by passing a pointer or the derived type itself.
As noted already in other answers, the purpose of the program seems to separate the allocation of the derived type and its components from each other. One possible advantage of this strategy is the possibility to pass both, pointers and statically allocated derived types to the INIT
routine.
Upvotes: 1
Reputation: 21431
A few things may come into play...
When you provide a pointer as an actual argument to a procedure where the corresponding dummy argument does NOT have the POINTER attribute (the case here), the thing that is associated with the dummy argument is the target of the actual argument pointer. So in this case, the thing being passed is the object that GD
(in the main program) is pointing to - the thing that was allocated by the allocate statement. (When both the actual and dummy arguments have the POINTER argument, then the POINTER itself is "passed" - you can change what the POINTER points to and that change is reflected back in the calling scope.)
Because the GD
dummy argument inside the function has the target attribute, pointers inside the function can be pointed at the dummy argument. You don't show any declarations for such pointers, but perhaps they are in elided code. If nothing is ever pointed at the GD
dummy argument (including inside any procedures that might be called by the INIT function), then the TARGET attribute is superfluous, but harmless apart from inhibiting some optimisations.
Things that have the pointer attribute also (automatically by language rules) have the TARGET attribute - so GD
in the main program has the TARGET attribute. The fact that GD
in the main program and in the function BOTH have the target attribute may be relevant because...
When the dummy argument has the TARGET attribute and the thing passed as the actual argument has the TARGET attribute, then pointers associated with the dummy argument inside the procedure are also "usually" (there are exceptions/processor dependencies for coindexed things/non-contiguous arrays/vector subscripted sections too complicated for me to remember) associated with the corresponding actual argument. If a pointer is not a local variable (perhaps it is a pointer declared in a module) then this association survives past the end of the procedure. Perhaps that's relevant in the elided code. (Alternatively, if the the actual argument does not have the TARGET attribute, then any pointers associated with the dummy argument become undefined when the procedure ends.)
The components of the globalDATA
type are themselves pointers. Consequently, GD
in the main program is a pointer to something (that something being allocated by the single ALLOCATE statement in the main program) that itself contains pointers to other things (those other things being allocated by the numerous ALLOCATE statements in the function). You have two levels of pointer, hence two levels of ALLOCATE.
Before Fortran 2003 (or Fortran 95 with the "allocatable TR") you couldn't have ALLOCATABLE components in derived types, and you couldn't have ALLOCATABLE dummy arguments - when the need for dynamic allocation collided with these former restrictions you had to use pointers instead, even if you were only using the pointers as values. I strongly suspect your code dates from this era (Support for the allocatable TR became widespread about a decade ago). In very "modern" Fortran pointers are (should?) only used when you might want variables that point at other things (where other things includes "no thing").
Upvotes: 10
Reputation: 29391
With a pointer variable that is a user-defined type that itself contains pointers, you have to allocate (i.e., create the storage) both the overall variable and the component pointers. The components aren't automatically allocated when the overall variable is. Someone made a design choice to allocate the overall variable in the main program and the components in a subroutine. Maybe they thought that allocating the overall variable was simple but allocating all of the components was getting complicated and wanted to relegate that to a subroutine.
Upvotes: 3