I am trying to the assign value to a variable at the time of declaration and use that variable to declare the dimensions of some arrays as follows,
type (typ_A), intent (in) :: str_A
integer, parameter ::
x val_4 = (str_A%val_1 + str_A%val_2),
x val_5 = str_A%val_3
integer :: array_1(str_A%val_1, str_A%val_2), array_2(val_4, val_5)
In this code, the array_1 is declared properly with the expected sizes but the array_2 is not declared. Also, I am getting errors like "This symbol must be a defined parameter, an enumerator, or an argument of an inquiry function that evaluates to a compile-time constant."
Note - I can straight away use the expression of val_4 to declare array_2, but sometimes, the expression (str_A%val_1 + str_A%val_2 + ....) is very large and have to use it to define multiple arrays. Hence, for better readability and less number of lines, I want to put it in a variable (val_4 in this case)
A Fortran parameter must be computable at compile time, as the error message hints, and str_A%val_1 etc are not known at compile time.
You can't use a variable directly for this, but you can create a nested argument:
subroutine x (str_A)
type(whatever),intent(in)::str_A
call x_2 (str_A, str_A%val_1 + str_A%val_2)
contains
subroutine x_2 (str_A, mydim)
type(whatever),intent(in)::str_A
integer::mydim
integer:: ary1(mydim), ary2(mydim), ary3(mydim)
...
end subroutine x_2
end subroutine x
or if you can put at least the type and a helper function in a containing module you can do:
module t
type whatever ...
contains
pure function mydim(str_A)
integer::mydim
type(whatever),intent(in)::str_A
mydim = str_A%val_1 + str_A%val_2
end function mydim
subroutine y (str_A)
type(whatever),intent(in)::str_A
integer:: ary1(mydim(str_A)), ary2(mydim(str_A)), ary3(mydim(str_A))
...
end subroutine y
end module t
Added since you may not have considered/realized it:
Alternatively you could simply use ALLOCATABLE arrays instead; those can be allocated (not declared) with bounds computed by any runtime expression, including a local variable.
The answer of #dave_thompson_085 already answers the root for your problem.
A Fortran parameter must be computable at compile time
There are four possibilities to achieve the behaviour you want:
If you use the block statement you create a local, nested scope where you can declare new variables. You can then declare arrays that use runtime variables outside of the block for their shapes.
integer :: n
n = compute_at_runtime()
block
integer :: M(n)
! do something with M
end block
! M does not exist anymore
As #dave_thompson_085 also put it in his answer, you can use contained procedures with a local nested scope, to declare array with runtime variables that exist outside of the procedure.
Or you use allocatable for dynamic memory and automatic destruction/deallocation.
Or you use pointer for dynamic memory, but then you have to manually cleanup.
Related
I'm trying to make a custom data-type constructor by overloading the type name. However, when making the call, the default constructor gets called instead. I cannot understand what I am doing wrong.
Here's a code snippet that has the problem.
module test
type, public :: foo
character(512) :: str
logical :: flag1
logical :: flag2
end type
! overload foo data-type
interface foo
module procedure make_foo
end interface
contains
! custom constructor
function make_foo(str, flag1) result(self)
implicit none
type(foo) :: self
character(512), intent(in) :: str
logical, intent(in), optional :: flag1
self % str = str ! this needs to be passed
self % flag1 = .false. ! this needs to be optional, and is false by default
if (present(flag1)) self % flag1 = flag1
self % flag2 = .false. ! this cannot be passed and is always false by default
end function
end module
program tmp
use test
implicit none
type(foo) :: a
a = foo("hello") ! error here
end program
I want to have a custom constructor that requires str to be passed, that allows for the optional specification of flag1 and that deals with flag2 always by itself.
When testing the data-type using its constructor, it uses the default constructor and it complains for missing components.
No initializer for component 'flag1' given in the structure constructor at (1)
I am using gfortran 10.2.0
The assignment statement
a = foo("hello")
is taken to be a reference to the generic foo if possible, and a reference to the default structure constructor for the type foo otherwise.
In the case here, the generic foo has one specific interface: make_foo. For reference to the generic foo we need foo("hello") to be consistent with make_foo.
The dummy argument str is declared as character(512) and so we are not allowed to reference it with actual argument the literal constant "Hello": that constant is (much) too short.1
The compiler falls back to the default structure constructor, and then (rightly) complains that there are components without default initialization which haven't been given values.
This reference can be fixed in one of two ways:
provide an actual argument of length at least 512
make str shorter, or better, assumed length
To address a concern about assumed length str: even if it's length 5, it still can be used in the assignment self%str = str as it will be padded with blanks at the end to make its length up to the 512 of self%str.
Whether that's strictly sufficient to deem the reference "inconsistent" is irrelevant: in trying to do this one doesn't have a Fortran program, so one can't demand the compiler tries that reference.
Is the following code where a local, saved variable is exposed to an outside scope valid Fortran(>=2003) code?
I intentionally did not specify a year for the standard. If the answers differ for different standards, assuming that pointers are supported, I would be also happy to hear the answer.
program test_save
implicit none
integer, pointer :: ptr
ptr => get_number(5)
write(*, *) ptr
contains
function get_number(n) result(res)
integer, intent(in) :: n
integer, pointer :: res
integer, target, save :: internal_n
internal_n = n
res => internal_n
end function
end program
The point to consider is whether the target of res remains defined when the function exits (F2018 19.6.6p1(16)). Because the target has the SAVE attribute, it does remain defined (F2018 19.6.6p1(3)), and therefore the pointer remains defined.
Consider the following class structure, which involves three separate modules:
!----------------------- in file a.f
module parent_body_mod
type :: face
class(parent_body), pointer :: bPtr
end type
type, abstract :: parent_body
integer i
type(face) :: f
end type
end module parent_body_mod
!------------------------ in file b.f
module body_mod
use parent_body_mod
type, extends(parent_body) :: body
end type
interface body
procedure :: new_body
end interface
contains
function new_body() result(b)
type(body), target :: b
b%i = 123
b%f%bPtr => b
end function
end module body_mod
!--------------------------- in file c.f
module body_group_mod
use body_mod
type :: body_group
type(body), allocatable :: b
end type
interface body_group
procedure :: new_body_group
end interface
contains
function new_body_group() result(bg)
type(body_group) :: bg
allocate(bg%b)
bg%b = body()
end function
end module body_group_mod
!------------------- The main program
use body_group_mod
type(body_group) :: my_bg
my_bg = body_group()
print *, my_bg%b%f%bPtr%i
end
!--------------------------------------
The expected output is 123, whereas the actual output is something random. The code is compiled using ifort version 18.0.1. Note that the same issue doesn't happen when using "body" class itself, i.e. the following works just fine:
type(body), allocatable :: my_b
allocate(my_b)
my_b = body()
print *, my_b%f%bPtr%i ! This produces 123 as expected.
Any help is appreciated.
The code is non conforming.
Pointers associated with unsaved local variables of a procedure become undefined when the execution of the procedure completes (F2008 16.5.2.5 (5)). The function result b in function new_body is considered such a local variable (F2008 1.3.154.1), hence the pointer component b%f%bPtr becomes undefined after the function call.
Function results are a little special compare to other local unsaved variables, in that their value is available longer than the variable exists - see F2008 Note 12.41 for some discussion.
Another way of thinking of the problems is that with the statement bg%b = body(), the body on the left hand side is a different object from the body on the right hand side. The assignment just copies the value of the right hand side object - once that assignment is complete, the right hand side object ceases to exist. Nowhere is there code to say that when the value of a body object is transferred - the pointer component needs to be updated to reference the left hand side variable being assigned to. Also note that the left hand side bg%b does not have the TARGET attribute - so there is no way that a pointer can be validly associated with it anyway.
Here is an illustration of the problem I have been having in the last days. Suppose you have the following structures:
TYPE superImportant
INTEGER(C_INT) :: one
END TYPE superImportant
TYPE lessImportant
TYPE(superImportant), POINTER :: ptr
END TYPE lessImportant
TYPE(superImportant), DIMENSION(:), ALLOCATABLE, TARGET :: superStruct
TYPE(lessImportant), DIMENSION(:), ALLOCATABLE :: lesserStruct
At some point in the code, superStruct and lesserStruct are allocated to sizes superSize and lesserSize, and for each element of lesserStruct, something like this is done:
lesserStruct(lesserNumber)%ptr => superStruct(superNumber)
Then, a bit further in the code, I need to re-allocate both superStruct and lesserStruct to larger sizes. So I do the following:
TYPE(superImportant), DIMENSION(:), ALLOCATABLE, TARGET :: tempSuperStruct
TYPE(lessImportant), DIMENSION(:), ALLOCATABLE :: tempLesserStruct
ALLOCATE(tempLesserStruct(lesserSize + newLesserSize))
tempLesserStruct(1:lesserSize) = lesserStruct(1:lesserSize)
CALL MOVE_ALLOC(tempLesserStruct, lesserStruct)
which works like a charm : all pointers are correctly copied and I can access lesserStruct(.)%ptr for all elements in 1:lesserSize.
However, if I try to do the same with the other structure :
ALLOCATE(tempSuperStruct(superSize + newSuperSize))
tempSuperStruct(1:superSize) = superStruct(1:superSize)
CALL MOVE_ALLOC(tempSuperStruct, superStruct)
Then I can no longer access lesserStruct(.)%ptr. I identified that this is because of the line with the =. Note that if I do instead:
CALL MOVE_ALLOC(superStruct,tempSuperStruct)
I can still access lesserStruct(.)%ptr, because, as said in the standard : If to has the TARGET attribute, any pointer associated with from at the time of the call to MOVE_ALLOC becomes correspondingly associated with to. Unfortunately, I still have to do an = step after that, during which I lose my pointer association.
So here is my question : is there a way to copy my structure in a "MOVE_ALLOC fashion" so the pointers associated with it will follow automatically ? More generally, is there a better solution to extend the size of my structures ?
Thanks in advance !
I think you are misunderstanding the use of MOVE_ALLOC.
When you reallocate the lesserStruct using a temporary allocatable and MOVE_ALLOC, in fact, you are preserving the pointers and this is why you can still use them after this step.
When, in the second step, you are reallocating the superStruct using the temporary allocatable and the MOVE_ALLOC, you are CHANGING the memory location where superStruct stores its values.
When, in step 2, instead you simple do a
CALL MOVE_ALLOC(superStruct,tempSuperStruct)
What you are doing is passing the allocation from "superStruct" to "tempSuperStruct". Now, superStruct is deallocated and tempSuperStruct now holds the allocated space. But if you look to it, you will see that it has only 'superSize' elements, and not 'superSize + newSuperSize'. The lesseStruct still works (At least to the superSize element), because the memory space to where is points is still being used in the same way.
And so, the reason why you misunderstand the standard, is that any pointer associated with the FROM will be associated with the TO if it has the TARGET attribute. In your case, any pointer associated with the "tempSuperStructure" that is your FROM will be associated with the superStructure after the MOVE_ALLOC, not the pointers from lesserStruct...
See?
In your case, the only way I see is to re-assign the pointers to the elements of superStruct after the reallocation of lesserStruct.
No - you will need to rethink your approach.
If the pointer is always associated with elements in a particular array, which seems to be the case here, then you can just store the index of the element of interest. Fortran has very good support for arrays and array indexing operations, so you might as well take advantage of this support.
An alternative is to hold the individual elements of the target array as persistent scalar objects, that are effectively not moved, rather than assigned, when you expand the target array.
TYPE superImportant
INTEGER(C_INT) :: one
END TYPE superImportant
TYPE superImportantWrapper
! Using a pointer to reference the object, because
! it ensures the target is a TARGET, and simplifies
! assignment. ALLOCATABLE is also a possibility,
! with other changes.
TYPE(superImportant), POINTER :: item => NULL()
CONTAINS
! Elided code to deallocate the item component.
FINAL :: wrapper_Final
END TYPE supertImportantWrapper
TYPE lessImportant
TYPE(superImportant), POINTER :: ptr
END TYPE lessImportant
! The super array is of the wrapper type.
TYPE(superImportantWrapper), DIMENSION(:), ALLOCATABLE, TARGET &
:: superStruct
TYPE(lessImportant), DIMENSION(:), ALLOCATABLE :: lesserStruct
! Elided code to populate each scalar component of the wrapper type.
DO i = 1, xxx ; ALLOCATE(superStruct(i)%item) ; ... ; END DO
! Point at the component, not the wrapper object.
lesserStruct(lesserNumber)%ptr => superStruct(superNumber)%item
...
! Time to grow? Again, array operations on the wrapper.
TYPE(superImportantWrapper), DIMENSION(:), ALLOCATABLE, TARGET :: tempSuperStruct
ALLOCATE(tempSuperStruct(superSize + newSuperSize))
! Intrinsic assignment of the pointer component means it maintains
! the reference to the same object that lesser struct is referencing.
tempSuperStruct(1:superSize) = superStruct(1:superSize)
CALL MOVE_ALLOC(tempSuperStruct, superStruct)
Consider the following FORTRAN program:
program functiontest
implicit none
real :: x, square
print *, "Enter a number to square"
read (*,*) x
print *, square(x)
end program functiontest
real function square(x)
real :: x
square = x * x
end function square
Why do I need to declare square to be real inside program functiontest? Haven't I already declared it a real function in its definition?
Why did the authors of FORTRAN make this design decision?
No, actually in your example you haven't declared it a real function inside the program, but it's an external function to the program. If you defined your function inside the program, as follows, or put it in a module and used it, you wouldn't have to specify it's a real function twice.
program functiontest
implicit none
real :: x
print *, "Enter a number to square"
read (*,*) x
print *, square(x)
contains
real function square(x)
real :: x
square = x * x
end function square
end program functiontest
As for why it also works the way you wrote it, it is for backwards compatibility with Fortran 77.
Put the function in a module and use the module, as shown below. Then you don't need to declare the function in the main program.
module foo
contains
real function square(x)
real :: x
square = x * x
end function square
end module foo
program functiontest
use foo
implicit none
real :: x
print *, "Enter a number to square"
read (*,*) x
print *, square(x)
end program functiontest
The square inside the function is a variable, and is not the function name. Since it is a variable, it must be declared with the right type.
I think you need to declare the type in functionlist because "in general" compiler doesn't know the type of "square". Consider a case that you have function "square" defined in a separate file, i.e. "square.f", and functionlist in another file "functionlist.f". In that case you need to compile each of those files separately and create two object file, i.e. square.o and functionlist.o. In this scenario, compiler has no clue about the "square" type, when compiling for "functionlist.o", unless you explicitly define it.
So you might ask why compiler needs to know the type of square in the first place. The answer, I think, is related to memory allocation, casting the type (e.g. when you are assigning the results to a real*8), ....
Note that, this is also common in C. There, you either need to define the prototype (in foo.h), in which the type of square is declared, or place all the functions in a file such that compiler "sees" square first.