Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

type to class conversion in call with optional arguments

Martin1
New Contributor I
2,841 Views
Out of curiosity, does anybody know whether the following code is standard conform or not. This was due to a minor oversight, but took a while to figure out, as the ifort compiler does not give any meaningful error messages but crashs with a segfault, whereas gfortran just works. The problem is that in sub1, argument p is declared with type(t) and in sub2 it is declared with class(t). program optional type :: t integer, pointer :: x end type t type(t) :: a integer, pointer :: y allocate(a%x, source=1) call sub1(a, c=y) contains subroutine sub1(a, p, c) class(t), intent(in) :: a type(t), optional, intent(out) :: p integer, pointer, optional, intent(out) :: c call sub2(a, p, c) end subroutine sub1 subroutine sub2(a, p, c) class(t), target, intent(in) :: a class(t), optional, intent(out) :: p integer, pointer, optional, intent(out) :: c if (present(p)) then p%x => a%x else c => a%x end if end subroutine sub2 end program optional
0 Kudos
28 Replies
IanH
Honored Contributor II
719 Views

may.ka wrote:

As an example, the automatic allocation in f08 is handy, but who knows what the compiler does if an array is copied into another  array which has the attribute "allocatable" but has been allocated some zillion lines way. Maybe the compiler will just newly allocate it. In some applications this may induce a slow down which renders the program unusable. In some application one may lose pointer association status.

"Automatic allocation" was introduced in Fortran 2003.

Reallocation only happens when the right hand value does not match the shape, length parameters or dynamic type of the left handle side variable being assigned to.  Code that relied on such an assignment with mismatching left and right sides working without reallocation, had a rather serious bug.  It may have run faster (if it didn't crash), but it also would have been wrong. 

0 Kudos
FortranFan
Honored Contributor III
719 Views

may.ka wrote:

.. The code snippet below implements a matrix class with two sibs, a class of squared symmetric matrices and a class of factors of squared symmetric matrices. Both are direct offspring of the parent class This is because both have different arguments for their initialization thus making the factor a child of the squared_symmetric doesn't work (without inventing procedure name extensions).

In the current implementation every type sits in its own module. This would not compile ..

Your case, as shown though, looks more like an OO design issue rather than any Fortran-specific limitations.  Studying design patterns that have been used reliably in numerical modeling might help deconvolute the interdependencies with your symmetric matrix subclasses and aid in writing more stream-lined structures.

0 Kudos
may_ka
Beginner
719 Views

@fortranfan

please be reminded that this is just a mickey-mouse example made for your specific request from which it is impossible to make inference about the larger frame within the example is operating. Code every now and then is use to reflect real life problems, and in real life situations were y=f(x,z) and z=f(x,y) are very common. You may give an example how to handle such case in the 1 Type 1 Module world.

0 Kudos
may_ka
Beginner
719 Views

Reallocation only happens when the right hand value does not match the shape, length parameters or dynamic type of the left handle side variable being assigned to.  Code that relied on such an assignment with mismatching left and right sides working without reallocation, had a rather serious bug.  It may have run faster (if it didn't crash), but it also would have been wrong.

Thanks IanH for the clarification. Your last two sentence are written in past tense. Do you refer to pre 2003 code??

0 Kudos
may_ka
Beginner
719 Views

Martin wrote:

When looking at submodules from this perspective it seems that this is a rather lame solution to these kind of problems.

Submodules are a step in the right direction. The problem is that the interface definition still has to sit in the module which can make modules very long. A nice inheritance tree can actually encompass all types/classes in a library. Thus all would have to sit in one single very very long module file (if they wanna know each other).

You mentioned the problem of "overengineering" inheritance trees, which I understand as a new subclass for every single new feature one adds to the class. This is indeed a problem because --from my current understanding-- initializers have to address all features of class not just a few. So if one would like to use them one is forced to engineer subclasses for every single new feature. Combined with the lack of multiple inheritance it is a feature which may be enjoyed by progammers with extended creativity in class naming.

0 Kudos
Martin1
New Contributor I
719 Views

may.ka wrote:

You mentioned the problem of "overengineering" inheritance trees, which I understand as a new subclass for every single new feature one adds to the class. This is indeed a problem because --from my current understanding-- initializers have to address all features of class not just a few.

I might completely misunderstand you. But you can always call the initialiser of the parent class, even if overloaded (like call x%type_parent%init(...)). A notable exception is if type_parent is abstract, but implements init, in which case the initialiser routine needs to have a different name and overloading is not possible (for whatever reason the committee had to forbid this...). So a derived class needs to initialise only what it adds to the parent class??

Otherwise I was just generally refering to the fact that often components are more flexible and cleaner than inheritance and also the SOA vs AOS, which often forbids to abstract small units like grid points in FDM or cells in mesh based methods (e.g. to distinguish boundary and interior points/cells).

 

0 Kudos
may_ka
Beginner
719 Views

Martin wrote:

 

I might completely misunderstand you. But you can always call the initialiser of the parent class, even if overloaded (like call x%type_parent%init(...)). A notable exception is if type_parent is abstract, but implements init, in which case the initialiser routine needs to have a different name and overloading is not possible (for whatever reason the committee had to forbid this...). So a derived class needs to initialise only what it adds to the parent class??

 

Otherwise I was just generally refering to the fact that often components are more flexible and cleaner than inheritance and also the SOA vs AOS, which often forbids to abstract small units like grid points in FDM or cells in mesh based methods (e.g. to distinguish boundary and interior points/cells).

 

What I was referring to was some thing like scenario A:

Module TestAA
  Implicit None
  Type parent
    Integer :: i,j
  End type parent
End Module TestAA
Program Test
  use Testaa
  type(parent) :: a
  a=parent(i=1)
end Program Test

which does not work. To use an initializer one would have to do either

a=parent(i=1,j=1)

 

or extend the class hierarchy putting feature j into a new subclass

Module TestAA
  Implicit None
  Type parent
    Integer :: i
  End type parent
  Type, extends(parent) :: child
    integer :: j
  End type child
End Module TestAA
Program Test
  use Testaa
  type(parent) :: a
  type(child) :: b
  a=parent(i=1)
  b=child(i=1,y=1)
end Program Test

 

and then decide whether one wants to use parent or child.

......................... However, thanks to IanH (https://stackoverflow.com/questions/32876256/how-to-initialize-fortran-derived-type-parameter-variable-containing-an-allocata) I just found that one can use the Null() function to omit a component from the initialisation.

a=parent(i=1,j=Null())

While this is a useful feature classes may have several dozen components of which many are set by calculating values from other (e.g. in the first of the above example j might always be calculated from i). From my current understanding I then have to put a null() value into the initializer call for every single component which seems a bit tedious.

 

0 Kudos
FortranFan
Honored Contributor III
719 Views

may.ka wrote:

.. What I was referring to was some thing like scenario A: ..

which does not work. To use an initializer one would have to do either

a=parent(i=1,j=1)

..

Fortran does come across as somewhat limited in the concept of 'object instantiation'  compared to other leading languages in use in scientific/technical computing with the OO paradigm, particularly when it comes to derived types.  Hopefully that will change circa year 2035 with Fortran 202X standard revision allowing for ~10 years for reasonably usable compiler implementations) or year 204X with the standard update thereafter.

In the mean time, you can make use of component initialization and/or the facility to provide a generic interface with the same name as the derived type to help with setting structure construction.

   type parent
      integer :: i=0, j=1
   end type parent

And/or

   type parent
      integer :: i=0, j=1
   end type parent
   interface parant
      module procedure construct_parent
   end interface
contains
   function construct_parent( i, j ) result ( r )
      integer, intent(in), optional :: i
      integer, intent(in), optional :: j
      type(parent) :: r
      .. ! instructions elided
   end function

And with abstract types, as Martin indicated, a type-bound procedure that can be invoked by extensions as ..%<abstract parent>%init_xx(..).

0 Kudos
Reply