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

Polymorphism in FORTRAN2003

holysword
Novice
600 Views
Hi everybody.
I would like to know if its possible to perform both upcasting and downcasting in with current version of ifort. For instance, the following code "almost" does the job:
[fxfortran]program main

type type_O
	integer :: O
end type type_O

type, extends(type_O) :: type_A
	real :: A
end type type_A

  class(type_O), POINTER :: my_O
  type(type_A),   TARGET :: my_A1
  type(type_A)           :: my_A2
  
  
  my_A1%O = 1
  my_A1%A = 1.56
  
  PRINT *,'Assignment: ', my_A1%O, my_A1%A
  
  ! Upcast
  my_O => my_A1
  
  PRINT *,'Upcasting: ', my_O%O
  
  my_A2 = TRANSFER(my_O,my_A2)
  
  PRINT *,'Downcasting: ', my_A2%O, my_A2%A
  
end program [/fxfortran]
Unfortunatelly it prints my_A2 = 0 instead of 1.56, that is, the information is "lost" during the downcast. I have read some "tricks" using the TRANSFER function, involving the creation of some kind of hash table, but that wouldn't work for me - in the application that I am targeting, my_A2%A and my_A2%O are huge arrays, and it would be prohibitive to "copy" this information even once.
Would anyone know how to workaround that? Thank you in advance!
0 Kudos
1 Solution
Hirchert__Kurt_W
New Contributor II
600 Views
Quoting holysword
In some other languages it would be trivial to do that:
my_A2 = (type_A) my_O
The compiler knows that it may generate an error, but it just "trusts the programmer", which I think its not the case in FORTRAN!

It's trivial, if slightly more verbose, in Fortran 2003:

SELECT TYPE (my_O)
TYPE IS (type_A)
my_A2 = my_O ! in this block, my_O is effectively declared to be type_A
END SELECT

This accomplishes the same thing, but it is different from the code you wrote in that it will not attempt the assignment if the dynamic type of my_O isn't type_A (i.e., it prevents you from shooting yourself in the foot).

The Fortran 2003 construct is also different in that you can execute multiple statements after establishing my_O to be type_A, and you can provide alternative blocks for other types and for the case that my_O isn't any of the types you are testing for. If the expression inside SELECT TYPE is more complicated that a simple name, you must also provide a simple name to stand for it inside the construct.

-Kurt

P.S. In case it isn't clear from what I've already said, although you are not allowed to write my_O%A in code outside this SELECT TYPE, inside the block following TYPE IS (type_A), you are allowed to do so. This would also be true if the guard statement were CLASS IS (type_A).

View solution in original post

0 Kudos
6 Replies
mecej4
Honored Contributor III
600 Views
From the Fortran 2003 Standard, section 13.7.121:

If the physical representation of the result is larger than source, the result contains source's bit pattern in its right-most bits; the left-most bits of the result are undefined.

From the Intel Fortran documentation, pertinent to the use of TRANSFER in Line-26:

If the physical representation of the result is larger than source, the result contains source's bit pattern in its right-most bits; the left-most bits of the result are undefined.


Since the type of my_A2 is an extension of that of my_O, the component of the converted type that was not present in the base class has to be undefined.

I have misgivings about Line-22 as well, although I cannot cite the standard on this one. The situation is similar to assigning x :: REAL(4) to y :: REAL(8), causing 29 bits of precision to be lost, and then assigning y to x. The new value of y will be different from the original.
[fxfortran]      program lose
real :: x
double precision :: y
y = acos(-1d0)
write(*,10)y
x=y
write(*,10)x
y=x
write(*,10)y
10 format(F20.15)
end

3.141592653589793
3.141592741012573
3.141592741012573[/fxfortran]
I could have used y = TRANSFER (x,y) on Line-8, but that could be considered provocative.
0 Kudos
holysword
Novice
600 Views
Thank you for answering mecej4. Yes, I heard that in FORTRAN2003 there is no real "casting", they have "molding", which works differentely.
Do you know any way to workaround it?
EDIT#1:Regarding line 22, it compiles and works here. Using SIZEOF intrinsic function, I can see also that my_O has the same size as my_A1 - and as well my_A2.
0 Kudos
IanH
Honored Contributor II
600 Views
I don't follow what you are trying to do, but to go from a polymorphic pointer (such as my_O) to the target object as an extension of the declared type of the pointer (which is what I'd regard as downcasting), use the SELECT TYPE construct. Line 22 is ok - its just pointer assignment.

Your use of transfer (which I'd think unlikely to work, but transfer scares me at the best of times) doesn't avoid copying as line 26 is still an assignment statement.
0 Kudos
holysword
Novice
600 Views
Hi IanH.
The idea was to get the information in the my_A1%A variable through my_O. Using SELECT TYPE wouldn't allow me to attempt something like my_O%A, since my_O doesn't have such a member.
In some other languages it would be trivial to do that:
my_A2 = (type_A) my_O
The compiler knows that it may generate an error, but it just "trusts the programmer", which I think its not the case in FORTRAN!
0 Kudos
Hirchert__Kurt_W
New Contributor II
601 Views
Quoting holysword
In some other languages it would be trivial to do that:
my_A2 = (type_A) my_O
The compiler knows that it may generate an error, but it just "trusts the programmer", which I think its not the case in FORTRAN!

It's trivial, if slightly more verbose, in Fortran 2003:

SELECT TYPE (my_O)
TYPE IS (type_A)
my_A2 = my_O ! in this block, my_O is effectively declared to be type_A
END SELECT

This accomplishes the same thing, but it is different from the code you wrote in that it will not attempt the assignment if the dynamic type of my_O isn't type_A (i.e., it prevents you from shooting yourself in the foot).

The Fortran 2003 construct is also different in that you can execute multiple statements after establishing my_O to be type_A, and you can provide alternative blocks for other types and for the case that my_O isn't any of the types you are testing for. If the expression inside SELECT TYPE is more complicated that a simple name, you must also provide a simple name to stand for it inside the construct.

-Kurt

P.S. In case it isn't clear from what I've already said, although you are not allowed to write my_O%A in code outside this SELECT TYPE, inside the block following TYPE IS (type_A), you are allowed to do so. This would also be true if the guard statement were CLASS IS (type_A).

0 Kudos
holysword
Novice
600 Views
Yes, you are absolutely right! Using SELECT TYPE works in this case. Unfortunatelly I was misled by the previous uses I saw of SELECT TYPE (maybe they were just not clear).
If I understood it correctly, in this case the SELECT TYPE is just to make sure that no error will be throw at runtime - if the assignment is invalid it will just skip the SELECT TYPE. As I said before, FORTRAN doesn't trust the programmer! (it is okay, the programmer usually shoot their own foot).
0 Kudos
Reply