Related
I am learning oop in fortran.
This is my code and the output surprise me :
module my_module
implicit none
type basic_t
real :: basic
end type basic_t
type, extends(basic_t) :: extended_t
real :: extended
end type extended_t
interface print_type
module procedure print_basic_type
module procedure print_extended_type
end interface print_type
contains
subroutine print_basic_type(basic)
type(basic_t), intent(in) :: basic
print *, 'in sub : print_basic_type'
end subroutine print_basic_type
subroutine print_extended_type(extended)
type(extended_t), intent(in) :: extended
print *, 'in sub : print_extended_type'
end subroutine print_extended_type
subroutine print_using_class(basic_or_extended)
class(basic_t), intent(in) :: basic_or_extended
select type (basic_or_extended)
type is (basic_t)
print *, 'the type is basic'
type is (extended_t)
print *, 'the type is extended'
end select
call print_type(basic_or_extended)
end subroutine print_using_class
end module my_module
program main
use my_module
type(basic_t) :: basic
type(extended_t) :: extended
call print_type(basic)
call print_type(extended)
print *,'------'
call print_using_class(basic)
print *,'------'
call print_using_class(extended)
end program main
This is the output :
in sub : print_basic_type
in sub : print_extended_type
------
the type is basic
in sub : print_basic_type
------
the type is extended
in sub : print_basic_type
When I use the interface print_type directly, it works. But when I call the subroutine print_using_class, it does not work for the extended type although the type is well recognized (cf select type before print_type call in subroutine print_using_class).
Is it the output expected ? And why ?
Thanks for answer.
Edit 1
As suggested in the comments, I should use "bindings". After many attempts, this is my new code.
module my_module
implicit none
type, abstract :: abstract_t
contains
procedure(print_abstract_t), deferred :: print_sub
end type abstract_t
abstract interface
subroutine print_abstract_t(this)
import abstract_t
class(abstract_t), intent(in) :: this
end subroutine print_abstract_t
end interface
type, extends(abstract_t) :: basic_t
real :: basic
contains
procedure :: print_sub => print_basic_type
end type basic_t
type, extends(basic_t) :: extended_t
real :: extended
contains
procedure :: print_sub => print_extended_type
end type extended_t
contains
subroutine print_basic_type(this)
class(basic_t), intent(in) :: this
print *, 'in sub : print_basic_type'
end subroutine print_basic_type
subroutine print_extended_type(this)
class(extended_t), intent(in) :: this
print *, 'in sub : print_extended_type'
end subroutine print_extended_type
subroutine print_using_class(basic_or_extended)
class(basic_t), intent(in) :: basic_or_extended
select type (basic_or_extended)
type is (basic_t)
print *, 'the type is basic'
type is (extended_t)
print *, 'the type is extended'
end select
call basic_or_extended%print_sub()
end subroutine print_using_class
end module my_module
program main
use my_module
type(basic_t) :: basic
type(extended_t) :: extended
call basic%print_sub()
call extended%print_sub()
print *,'------'
call print_using_class(basic)
print *,'------'
call print_using_class(extended)
end program main
I am surprised but it works. As I am learning OOP in fortran, any criticism would be appreciated.
In the subroutine print_using_class there is a reference to the generic print_type with actual argument basic_or_extended.
The generic print_type has two specific interfaces, print_basic_type and print_extended_type. (Because these specific procedures use non-polymorphic arguments they are not ambiguous (Fortran 2018, 15.4.3.4.5).) In the reference to the generic, we need to resolve the reference to a specific procedure. (F2018, 15.5.5.2)
Resolution to which of these two specific procedures is referenced is not based on dynamic type; resolution is based on declared type.
Why?
The relevant dummy arguments are:
type(basic_t) in print_basic_type
type(extended_t) in print_extended_type
The referenced specific procedure is the one where the dummy argument is type compatible with class(basic_or_extended) (F2018, 15.5.2.4). (Recall, only one specific can be consistently referenced.)
type(basic_t) is type compatible with class(basic_t); type(extended_t) is not (F2018, 7.3.2.3 p.5). The reference to the generic print_type is always to print_basic_type. (Note that a class(basic_t) is type compatible with a type(extended_t) but we need the dummy to be type compatible with the actual, not the actual to be type compatible with the dummy: type compatability is not symmetric. )
If you want to use a generic and resolve to the dynamic type of the actual argument, well you can't. What you can do is have another object of the desired declared type:
subroutine print_using_class(basic_or_extended)
class(basic_t), intent(in) :: basic_or_extended
select type (basic_or_extended)
type is (basic_t)
print *, 'the type is basic'
call print_type(basic_or_extended)
type is (extended_t)
print *, 'the type is extended'
call print_type(basic_or_extended)
end select
end subroutine print_using_class
But ideally, you wouldn't be doing this. Instead, you'll be using bindings and type-bound procedures where this dynamic resolution just falls out.
I have the following module with an allocatable variable which is defined in the module, allocated in a subroutine, and then also used in a second subroutine called by the first subroutine. In this situation do I have to pass the variable to the second subroutine and declare INTENT(inout)? Or since it's a global variable it doesn't need to be passed as an argument?
MODULE test
IMPLICIT NONE
SAVE
REAL,ALLOCATABLE,DIMENSION(:,:,:) :: total
CONTAINS
!--- 1st subroutine
SUBROUTINE my_subr1(n,m,z)
IMPLICIT NONE
INTEGER,INTENT(in) :: n,m,z
ALLOCATE(total (n,m,z))
total=.9
CALL my_subr2(n)
END SUBROUTINE my_subr1
!-- 2nd subroutine
SUBROUTINE my_subr2(n)
IMPLICIT NONE
INTEGER,INTENT(in) :: n
total(n,:,:)=total(n-1,:,:)
END SUBROUTINE my_subr2
END MODULE test
do I have to pass the variable to the second subroutine and declare INTENT(inout)?
No, you don't. Any variable decalred in the body of the module has the save attribute by default. You must, though, ensure that the second subroutine is only called after the first was executed, or else the program will fail because total would not be initialized yet.
All functions and subroutines declared in the module will have access to total by host association.
By the way, there are some issues you should address in your code, as mentioned by #PierredeBuyl in the comments:
Variables declared in the module body are saved by default; you should remove the SAVE statement.
Procedures declared in a module inherit the IMPLICIT directive from the module scope, there is no need to redeclare it in the subroutine if you won't change it.
You are missing the declaration of the arguments in my_subr1.
Hello I am trying to learn Object Oriented Programming using Fortran (all my codes are written in Fortran 90), I partially know C++ but I want to carry on using Fortran.
In Fortran you make classes employing modules. I am facing compiling errors when writing the integer, parameter :: dp = selected_real_kind(15,307) statement. Here goes my academic code. Its a short code that uses Abstract classes
module class_Rectangle
implicit none
integer, parameter :: dp = selected_real_kind(15,307)
type Rectangle
real(dp) :: a,b
end type Rectangle
contains
subroutine area_rectangle(area,info)
implicit none
real(dp), intent(out) :: area
type(Rectangle), intent(in) :: info
area = info%a * info%b
end subroutine area_rectangle
end module class_Rectangle
program Main
use class_Rectangle
use class_Circle
implicit none
integer, parameter :: dp = selected_real_kind(15,307)
interface compute_area
module procedure area_rectangle, area_circle
end interface compute_area
type(Rectangle) :: geoA
type(Circle) :: geoB
real(dp) :: area
geoA = Rectangle(2.0d0,4.0d0)
call area_rectangle(area,geoA)
write(*,*) 'Rectangle area:', area
geoB = Circle(1.0d0)
call area_circle(area,geoB)
write(*,*) 'Circle area:',area
end program Main
The message that the compiler returns me is the following:
integer, parameter :: dp = selected_real_kind(15,307)
1
Error: Name 'dp' at (1) is an ambiguous reference to 'dp' from module 'class_rectangle'
Main.f90:81.13:
real(dp) :: area
1
Error: Name 'dp' at (1) is an ambiguous reference to 'dp' from module 'class_rectangle'
Main.f90:84.30:
Any hint or advice is welcome.
Always try to use USE statement with ONLY. If you do so, you will not encounter the ambiguity error you get. In addition, you will know exactly what is being used in your code from each module, just by a quick look at the top lines of your code. Therefore, your main program header could look like this:
program Main
use class_Rectangle, only: Rectangle, area_rectangle
use class_Circle, only: Circle, area_circle
implicit none
integer, parameter :: dp = selected_real_kind(15,307)
interface compute_area
module procedure area_rectangle, area_circle
end interface
type(Rectangle) :: geoA
type(Circle) :: geoB
real(dp) :: area
geoA = Rectangle(2.0d0,4.0d0)
call area_rectangle(area,geoA)
write(*,*) 'Rectangle area:', area
geoB = Circle(1.0d0)
call area_circle(area,geoB)
write(*,*) 'Circle area:',area
end program Main
For a good tutorial on OOP programming in Fortran with some good examples, see the book "Modern Fortran Explained" by Metcalf et al.
Depending on your compiler, you should get pretty helpful messages
module a
end module a
program b
implicit none
use a
end program b
And compile:
$ gfortran mod_test.F90
mod_test.F90:6:9:
implicit none
2
use a
1
Error: USE statement at (1) cannot follow IMPLICIT NONE statement at (2)
Just omit: integer, parameter :: dp = selected_real_kind(15,307)
in the main program! By USEing class_Rectangle the main program it
shares this variable with the module (except you declare it private,
which is not what you want here I guess).
This is part of a main program
PROGRAM program1
USE method
USE variables
IMPLICIT NONE
:
CALL method_init(A,a1end,C)
:
END PROGRAM program1
The call to method_init, contained in the module method, "initializes" a method in that it builds arrays a1end and C form the array A (other calls to other procedures contained in the module should follow).
Arrays a1end and C are part of the method, so they are both declared in the method module; the array A is not part of the method (it could be "solved" with another method), so it is declared in the module variables.
The arrays C and a1end could be used by subroutines not contained in the method module, so they must be declared before the CONTAINS statement.
So, the subroutine contained in the method module could use these variables without using them as input/output variables, but this would make unclear the role of the subroutine (the call would be simply CALL method_init, so "How does this subroutine operate? Which arrays does it use? And which modify? ..."), so I prefer to have the call as CALL method_init(A,a1end,C).
This means that the module method is like this
MODULE method
IMPLICIT NONE
REAL, DIMENSION(:,:), ALLOCATABLE :: C ! declared here to be used...
REAL, DIMENSION(:,:), ALLOCATABLE :: a1end ! ...by procedures outside this module
:
CONTAINS
SUBROUTINE method_init(A,a1end,C)
IMPLICIT NONE
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(IN) :: A ! deferred shape (it's allocated elsewhere in the main program)j
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: C ! declared here to be used...
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: a1end ! ...as input/output variables
:
ALLOCATE(C( ),a1end( ))
:
END SUBROUTINE method_init
END MODULE method
I'd like to know if this is a correct way of programming. Is it just a matter of taste?
EDIT The question, in short, is:
Is a correct way of programming to use variables defined in a module as input/output arguments of procedure contained in the module itself? Or it is better to write subroutines with no arguments? Or everything is just a matter of taste?
The questions/answers linked by #Vladimir F make me think that yes, it' a matter of taste. Nevertheless none of these question touches the specific point I'm interested into (i.e. procedures that are in a module and use a variable defined in the module as input/output arguments).
My answer would be that you did the right choice as I would always recommend to write procedures with all its parameters and avoid using global variables (like C and a1end in your example).
However, many people in Fortran use to use global variables and would not bother passing these arguments, sometimes for wrong reason (like "it's faster to write").
But it is still totally correct to use global variable in a module if you imagine the latter as being a box containing its own specific and unique set of parameters. Maybe then you would want to specify them as Fortran parameter (with the associated keyword).
When you question yourself about passing arguments or using global variables for a function/subroutine, a simple choice would be whether or not your function is bound to be called/reused with other different parameters somewhere else in your code, or sometime later in your development process.
This is particularly true when you think of your module as a box that you can unplug and give to a mate for his own needs, then you might want your method_init to be more flexible, thus expliciting the arguments.
Note: in your example, I would recommend to name your subroutine arguments differently than your module's variables in order to avoid any confusion in the code and be able to use them without any conflict:
MODULE method
IMPLICIT NONE
REAL, DIMENSION(:,:), ALLOCATABLE :: C
REAL, DIMENSION(:,:), ALLOCATABLE :: a1end
CONTAINS
SUBROUTINE method_init(A,my_a1end,my_C)
IMPLICIT NONE ! Already specified above
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(IN) :: A
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: my_C
REAL, DIMENSION(:,:), ALLOCATABLE, INTENT(OUT) :: my_a1end
ALLOCATE(my_C( ),my_a1end( ))
! Here you can work with module's C and a1end
! Given that they have been effectively allocated
! You can also test whether C and my_C are the same object or not (pointerwise speaking)
END SUBROUTINE method_init
END MODULE method
I am trying to add access restrictions to some of the variables (using private attribute) in a module but I need to use those variables in subsequent invocations of routines within that module:
module MyMod
private
integer,allocatable :: A(:)
public :: init,calc
contains
subroutine init()
! allocating and initializing A
end subroutine init
subroutine calc()
! Using A
end subroutine
end module
Questions:
Is this true that the private variable will not be in the scope of the program which uses this module.
If the answer to 1 is yes, then I would think that I can use save attribute for this variable. Please correct me if I am wrong?
Is this the proper way to perform this task?
Yes, if you put a private statement into your module without any further specification, you set the default accessibility to private.
For the first question, the Fortran 2008 Standard (Cl. 4.5.2.2 §3) states that:
If a type definition is private, then the type name, and thus the structure constructor (4.5.10) for the type, are accessible only within the module containing the definition, and within its descendants.
So A will not be accessible from anywhere outside the module or submodule (descendant).
For the second question, yes - you can use save here. (This is not related to the accessibility attribute). In fact, starting with Fortran 2008, this is implied for module variables, see for the Fortran 2008 Standard (Cl. 5.3.16 §4) [thanks #francescalus]:
A variable, common block, or procedure pointer declared in the scoping unit of a main program, module, or submodule implicitly has the SAVE attribute, which may be confirmed by explicit specification [...]
If I understood your third question correctly, it is related to the initialization. You could realize this with a function/subroutine to which you pass an array for initialization:
module MyMod
! ...
contains
! ...
subroutine init( A_in )
implicit none
integer, intent(in) :: A_in(:)
! allocating and initializing A
allocate( A(size(A_in)) )
A = A_in
end subroutine
end module
Inside init() you create a copy of A_in which is only accessible within the module. As long as calc() is part of the module (or a submodule thereof), it has full access to A.
To add to the answer by Alexander Vogt an implication of the save attribute.
This attribute gives precisely the effect you seem to be after (from F2008 5.3.16):
The SAVE attribute specifies that a local variable of a program unit or subprogram retains its association status, allocation status, definition status, and value after execution of a RETURN or END statement unless [something not applicable here]
In your example, A is a local variable of the module (and so a program unit) MyMod.
This means that, after the call to init which, presumably, allocates A and sets values that status is retained after the subroutine returns. Those values are then available come the call to calc. Both init and calc, of course, refer to the same A through host association.
You mention Fortran 90, so there is a subtle change (again as mentioned in that other answer). Before Fortran 2008 module variables would require the save attribute explicitly giving in some circumstances for this effect to come about. There's no harm in giving the save attribute explicitly if you aren't sure how your compiler treats the code (and some would say it's good practice, anyway).
This is another way of accomplishing what you want to do while avoiding the 'save'.
module MyMod
private
public :: myType, init
type myType
private
integer, allocatable :: A(:)
end type myType
contains
subroutine init(obj, n)
type(myType), intent(inout) :: obj
integer, intent(in) :: n
allocate(obj%A(n), source=-9999999)
end subroutine init
end module MyMod
program test
use MyMod, only: myType, init
type(myType) :: m ! m is an opaque object
call init(m, 10)
end program test
In this example, m is an opaque object - I can create the object, pass it around, but not access its contents (the array A in this case). This way, I am hiding my data. As long as my object m is in scope, I can operate on the data hidden in the object through routines contained in the module myMod.
If you have access to a modern compiler that supports Fortran 2003, you can rewrite the module as
module MyMod
private
public :: myType
type myType
private
integer, allocatable :: A(:)
contains
procedure, public :: init
end type myType
contains
subroutine init(obj, n)
class(myType), intent(inout) :: obj
integer, intent(in) :: n
allocate(obj%A(n), source=-9999999)
end subroutine init
end module MyMod
program test
use MyMod, only: myType
type(myType) :: m ! m is an opaque object
call m%init(10)
end program test