I have a type for linked lists in Fortran. It is pretty useless, outside of demonstrating linked lists. I hope to derive a type from it that I can put to some good use. My base class is link and my derived class is useful_link. link doesn't do much except create a new link and receives a pointer to that link. My intent is that useful_link will create a new useful_link and also pass some useful data to its new creation. (And, as you might guess, do a bunch of other stuff that I don't have time or space to discuss.) My first pass at writing the code is:
module links
type link
type(link), pointer :: child => null()
contains
procedure :: reproduce
end type link
contains
subroutine reproduce(this, child_ptr)
class(link) :: this
class(link), pointer :: child_ptr
allocate(this%child)
child_ptr => this%child
end subroutine reproduce
end module links
module useful_links
use links
type, extends(link) :: useful_link
real*8 :: dataval
contains
procedure :: reproduce_w_data
end type useful_link
contains
subroutine reproduce_w_data(this, child_ptr, dataval)
class(useful_link) :: this
class(useful_link), pointer :: child_ptr
call this%link%reproduce(child_ptr) ! <--- problem!
child_ptr%dataval = dataval
end subroutine reproduce_w_data
end module useful_links
This does not compile. gfortran tells me "Error: Actual argument to ‘child_ptr’ ... must have the same declared type", and the problem is with the call to reproduce within the subroutine reproduce_w_data. Also, if I re-write the code to remove the child_ptr arguments, and try to assign dataval to the new link with
this%child%dataval = dataval
then gfortran tell me "‘dataval’ ... is not a member of the ‘link’ structure". Well, this all makes sense, but surely there is some way to make this work. Anyone know what it is?
BTW, I have seen examples that use the select type construct, but that is horribly unwieldy and, if I understand it, I would have to add a new option to reproduce in the link type every time I added a new inheriting type, which seems to me to defeat one of the purposes of OOP. Am I right about that? I'm pretty new to OOP in Fortan.
Finally, could anyone recommend a good reference on this? There seems to be many people out there who know this stuff well, but I can't find a place where it is all put together in one place.
Related
I am not sure if I wrote the question title right, but I did a MWE to explain me better what I would like to do:
module prueba
type :: basic
class(basic),pointer :: myself => null()
contains
procedure :: hello => basic_hello
end type
type,extends(basic) :: complicated
contains
procedure :: hello => complicated_hello
end type
contains
subroutine basic_hello(a)
class(basic), target :: a
a%myself=>a ! <----- XXX
print *, 'I am basic'
end subroutine
subroutine complicated_hello(a)
class(complicated), target :: a
call a%basic%hello()
print *, 'I am complicated'
end subroutine
end module
program main
use prueba
type(complicated) :: a
call a%hello()
! Returns:
! I am basic
! I am complicated
call a%myself%hello()
! Returns:
! I am basic
end program
I would like to set the pointer a%myself in the basic level, making it polymoprhic, so then I can run any of the extended hello procedure. That is, I would like to also get "I am complicated" legend in the second call (through the pointer). This might no have sense in the MWE, but I think it actually does in my real code. Since this approach is not working, I would like to know why, and what other alternative is possible. Thank you.
The main reason is in the
call a%basic%hello()
that way you are passing a reference which is really type(basic) and hence the stored pointer in myself is just a pointer to that part that is of type(basic).
You need to do the assignment
a%myself=>a
in some context, where the a is really of dynamic type complicated even if polymorphic class(basic).
That could be in the outer scope, where you have access to the original type(complicated) variable or you could, e.g., do
call basic_hello(a)
instead of the call a%basic%hello().
This passes the a that is the argument of complicated_hello(a) instead of the a%basic.
I'm very new to Fortran, and for my research I need to get a monster of a model running, so I am learning as I am going along. So I'm sorry if I ask a "stupid" question.
I'm trying to compile (Mac OSX, from the command line) and I've already managed to solve a few things, but now I've come across something I am not sure how to fix. I think I get the idea behind the error, but again, not sure how to fix.
The model is huge, so I will only post the code sections that I think are relevant (though I could be wrong). I have a file with several subroutines, that starts with:
!==========================================================================================!
! This subroutine simply updates the budget variables. !
!------------------------------------------------------------------------------------------!
subroutine update_budget(csite,lsl,ipaa,ipaz)
use ed_state_vars, only : sitetype ! ! structure
implicit none
!----- Arguments -----------------------------------------------------------------------!
type(sitetype) , target :: csite
integer , intent(in) :: lsl
integer , intent(in) :: ipaa
integer , intent(in) :: ipaz
!----- Local variables. ----------------------------------------------------------------!
integer :: ipa
!----- External functions. -------------------------------------------------------------!
real , external :: compute_water_storage
real , external :: compute_energy_storage
real , external :: compute_co2_storage
!---------------------------------------------------------------------------------------!
do ipa=ipaa,ipaz
!------------------------------------------------------------------------------------!
! Computing the storage terms for CO2, energy, and water budgets. !
!------------------------------------------------------------------------------------!
csite%co2budget_initialstorage(ipa) = compute_co2_storage(csite,ipa)
csite%wbudget_initialstorage(ipa) = compute_water_storage(csite,lsl,ipa)
csite%ebudget_initialstorage(ipa) = compute_energy_storage(csite,lsl,ipa)
end do
return
end subroutine update_budget
!==========================================================================================!
!==========================================================================================!
I get error messages along the lines of
budget_utils.f90:20.54:
real , external :: compute_co2_storage
1
Error: Dummy argument 'csite' of procedure 'compute_co2_storage' at (1) has an attribute that requires an explicit interface for this procedure
(I get a bunch of them, but they are essentially all the same). Now, looking at ed_state_vars.f90 (which is "used" in the subroutine), I find
!============================================================================!
!============================================================================!
!---------------------------------------------------------------------------!
! Site type:
! The following are the patch level arrays that populate the current site.
!---------------------------------------------------------------------------!
type sitetype
integer :: npatches
! The global index of the first cohort in all patches
integer,pointer,dimension(:) :: paco_id
! The number of cohorts in each patch
integer,pointer,dimension(:) :: paco_n
! Global index of the first patch in this vector, across all patches
! on the grid
integer :: paglob_id
! The patches containing the cohort arrays
type(patchtype),pointer,dimension(:) :: patch
Etc etc - this goes one for another 500 lines or so.
So to get to the point, it seems like the original subroutine needs an explicit interface for its procedures in order to be able to use the (dummy) argument csite. Again, I am SO NEW to Fortran, but I am really trying to understand how it "thinks". I have been searching what it means to have an explicit interface, when (and how!) to use it etc. But I can't figure out how it applies in my case. Should I maybe use a different compiler (Intel?). Any hints?
Edit: So csite is declared a target in all procedures and from the declaration type(site type) contains a whole bunch of pointers, as specified in sitetype. But sitetype is being properly used from another module (ed_state_vars.f90) in all procedures. So I am still confused why it gives me the explicit interface error?
"explicit interface" means that the interface to the procedure (subroutine or function) is declared to the compiler. This allows the compiler to check consistency of arguments between calls to the procedure and the actual procedure. This can find a lot of programmer mistakes. You can do this writing out the interface with an interface statement but there is a far easier method: place the procedure into a module and use that module from any other entity that calls it -- from the main program or any procedure that is itself not in the module. But you don't use a procedure from another procedure in the same module -- they are automatically known to each other.
Placing a procedure into a module automatically makes its interface known to the compiler and available for cross-checking when it is useed. This is easier and less prone to mistakes than writing an interface. With an interface, you have to duplicate the procedure argument list. Then if you revise the procedure, you also have to revise the calls (of course!) but also the interface.
An explicit interface (interface statement or module) is required when you use "advanced" arguments. Otherwise the compiler doesn't know to generate the correct call
If you have a procedure that is useed, you shouldn't describe it with external. There are very few uses of external in modern Fortran -- so, remove the external attributes, put all of your procedures into a module, and use them.
I ran into the same problems you encountered whilst I was trying to install ED2 on my mac 10.9. I fixed it by including all the subroutines in that file in a module, that is:
module mymodule
contains
subroutine update_budget(csite,lsl,ipaa,ipaz)
other subroutines ecc.
end module mymodule
The same thing had to be done to some 10 to 15 other files in the package.
I have compiled all the files and produced the corresponding object files but now I am getting errors about undefined symbols. However I suspect these are independent of the modifications so if someone has the patience this might be a way to solve at least the interface problem.
I recently started to dive into OOP with Fortran. I have a type hierarchy where I have an abstract base type genericProblem and a derived type specificProblem. specificProblem has a subroutine that is not defined in genericProblem, called "InitializeWith" in my example.
It seems that I misunderstood the select type construct. I thought it was made specifically for such cases where I know I can expect the variable problem to be of a certain (derived) type of genericProblem and that the compiler should know that "I'm apparently of type specificProblem now function calls specific to that type are ok".
I am however getting a
This is not a field name that is defined in the encompassing structure.
error with the example below, so I am probably missing out something. Could someone point me in the right direction?
subroutine AssignSomething(problem,rhsTarget)
class(genericProblemT), intent(in out) :: problem
class(genericProblemT), target, intent(in) :: rhsTarget
select type (lhsProblem => problem)
type is (specificProblemT)
! Try setting up the rhsProblem:
call lhsProblem%InitializeWith(rhsTarget) ! Gives an error
class default
! give error for unexpected/unsupported type
throw some error
end select
end subroutine AssignSomething
I can extend a program by adding a module file in which I extend originally defined derived types like e.g.:
module mod1
type type1
real :: x
end type
end module
module mod2
use mod1
type,extends(type1) :: type2
contains
procedure,pass :: g
end type
contains
function g(y,e)
class(type2), intent(in) :: y
real,intent(in) :: e
g=y%x+e
end function
end module
program test
use mod2
type(type2) :: a
a%x=3e0
write(*,*) a%g(5e0)
end program
But with this solution I need to change the declaration of 'a' (type1->type2) in the calling program, each time when I'm adding another module. So my question is if there is a way around this, i.e. I can add a type bound procedure to a derived type in another module without changing the original name of the type.
I totally understand that this might not work since I could then declare a variable and extend its type later, what sounds problematic for me. So, I thought about the deferred statement. But this isn't really what I want, since I first have to ad it to the original definition and second I need to provide an interface and thus need to know about the variables of the later coming function (here g) already. However, maybe someone has a nice solution for this.
All this is of course to bring more structure in the program, especially when I think about different people working on one program at the same time, such a possibility to split workpackages seems rather useful.
You can rename entities that are use associated by using the renaming capability of the USE statement.
MODULE m2
USE m1, chicken => type1
TYPE, EXTENDS(chicken) :: type1
...
type1 in module m2 is a different type to type1 in module m1.
You could also do this renaming in the USE statement in your main program, or via some intermediate module.
If both type1 names are accessible in another scope and you reference the name type1, then your compiler will complain.
If you use this trick and other programmers read your code, then they might complain.
To some extent submodules would help you, but they are implemented in the most widely used compilers. You could defer the implementation of the procedure to the submodule, but you would have to specify the interface anyway.
It isn't possible in any other way as far as I know.
I've had a good look around the previous posts and I don't think that this topic has been covered, hopefully somebody can help.
I'm writing a code in fortran 2003, and using ifort. I have the following types which I need to manipulate, which I have design to give the flexibility that I need:
module parameters
double precision, target :: cur_x(3)
type fundamental
double precision, pointer :: x => null()
end type fundamental
type, extends(fundamental) :: ion
class(fundamental), pointer :: core => null()
end type ion
SAVE
end module parameters
The idea being that I build up a kind of a linked list of particles by using the previous in the list as the core of the next. Note that in reality I will have a large number of extensions to 'fundamental', all of which can be the 'core' of other particles. I want the calculated quantities, x, to be in an array together in physical memory as I will be addressing subsets of them in fairly complicated ways, for which I want to use another set of pointers to cur_x
The initialisation of the code goes like this, where I have added some diagnostic lines:
use parameters
type(fundamental), target :: electron, proton
type(ion), target :: hydrogen
write(*,*)associated(electron%x),associated(proton%x), &
& associated(hydrogen%core),associated(hydrogen%core%x)
electron%x => cur_x(1)
hydrogen%core => proton
proton%x => cur_x(2)
hydrogen%x => cur_x(3)
cur_x = 1.0
write(*,*)electron%x,proton%x,hydrogen%x,hydrogen%core%x
which prints
F F F T
1.0 1.0 1.0 <garbage>
Where I expect proton%x and hydrogen%core%x to be the same address in memory (cur_x(2)). So I have two questions
I have initialised all my pointers to be null. Why does
associated(hydrogen%core%x) give true? If I try and nullify this
pointer at the top of the code I get inconsistent results; using
nullify(hydrogen%core%x)
results in a segmentation fault. Performing
hydrogen%core%x => null()
allows the code to run, but associated(hydrogen%core%x) remains true
I have made sure to associate the list of pointers from parent to child, as suggested in this summary of surprising errors in fortran. The fact that proton%x is working but hydrogen%core%x gives garbage is something I don't understand.
I can work around the problem but this would sacrifice the generality that I will need for more complex calculations. I would also quite like to understand what is going wrong here.
Thanks for your help!
Jim
EDIT: added in 'target' properties for various things; note that this were always in the code, i just forgot them in transferring to this post
EDIT: To clarify, my main issue with the above code is that the final write command gives an uninitialised output for hydrogen%core%x, even following the association commands after the first write. Even though I initialise core as null in my type definitions, there seems to be a problem with it; and if I try and nullify it at the top of the code the program crashes.
The basic problem is that hydrogen%core is not associated. The effect of associated(hydrogen%core%x) is therefore indeterministic. You just can not access/query a field (x) of a derived type pointer (hydrogen%core), if the pointer is not associated e.g. points to null() instead of an existing derived type instance in memory. Your compiler may generate a code, which does not immediately crash when you try it, but whatever is done after that is indeterministic, as you probably have already overwritten some data at random memory address.
Actually, I compiled a self-containing version of your code (see below) with various compilers and I got immediate segfaults at the first write statement. Using appropriate check options one of the binaries even reports the reason being in referencing the disassociated pointer hydrogen%core. If you comment out the problematic associated() query (as in the code below) all binaries run fine.
Also, please note, that the variables electron and proton must have the target attribute, otherwise the code should not even compile at all. If your compiler compiles the code without complaints, you should probably think about changing to an other one.
module parameters
implicit none
save
type :: fundamental
double precision, pointer :: x => null()
end type fundamental
type, extends(fundamental) :: ion
class(fundamental), pointer :: core => null()
end type ion
end module parameters
program test
use parameters
implicit none
type(fundamental), target :: electron, proton
type(ion) :: hydrogen
double precision, target :: cur_x(3)
! If you remove the comment in the next statement, the program will be indeterministic
! and probably crash.
write(*,*) associated(electron%x),associated(proton%x), &
& associated(hydrogen%core)!, associated(hydrogen%core%x)
electron%x => cur_x(1)
hydrogen%core => proton
proton%x => cur_x(2)
hydrogen%x => cur_x(3)
cur_x(:) = 1.0
write(*,*)electron%x,proton%x,hydrogen%x,hydrogen%core%x
end program test
If hydrogen%core is not associated, then it is a programming error to reference (or define) hydrogen%core%x as you do so in the first write statement. With that programming error present, anything goes from that point on.
You don't say what version of ifort you are using but there have been (and I think are extant, if I recall recent posts on the Intel forums) compiler bugs to do with polymorphic pointers.
That aside, for hydrogen%core to be pointed at the local proton variable, then proton must have the TARGET attribute. I would expect the compiler to diagnose this.