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

More About F08 Casting

holysword
Novice
804 Views

I'm having some troubles understanding how to properly use some of the OO features of Fortran 2008. Follows attached a small code exemplifying my problem.

I basically have 3 types: FurList_T which extends ExtList_T, which extends List_T.

Because I will sometimes use procedures from ExtList_T and List_T from within FurList_T, I need a "downcast" methods. Those are implemented in cast_mini.f90.

I have expected that implementing a downcast from Cast_ListFur would suffice, because if I pass an ExtList_T polymorphic Object, it will be treated as a List_T object. However, ifort refuses to compile it:

cast_minimain.f90(13): error #6633: The type of the actual argument differs from the type of the dummy argument.   [PTR_EXT]
ptr_fur => Cast_ListFur(ptr_ext)
------------------------^
compilation aborted for cast_minimain.f90 (code 1)

This wouldn't annoy me so much if I could create an interface for that (in cast_mini.f90, lines 18~21) but ifort refuses to compile the interface as well:

cast_mini.f90(51): error #5286: Ambiguous generic interface CAST_ANY: previously declared specific procedure CAST_LISTFUR is not distinguishable from this declaration. [CAST_EXTFUR]
FUNCTION Cast_ExtFur(in) RESULT(out)
---------^
compilation aborted for cast_mini.f90 (code 1)

Is this behaviour expected? Any suggestions on workarounds?

0 Kudos
8 Replies
FortranFan
Honored Contributor II
804 Views

I don't understand your statement, " I will sometimes use procedures from ExtList_T and List_T from within FurList_T, I need a "downcast" methods".

Instead of creating explicit options to "downcast", look into the so-called type-bound procedures, that might be really what you need:

https://software.intel.com/en-us/fortran-compiler-19.0-developer-guide-and-reference-type-bound-procedures

https://software.intel.com/en-us/fortran-compiler-19.0-developer-guide-and-reference-type-extension

0 Kudos
holysword
Novice
804 Views

FortranFan wrote:

I don't understand your statement, " I will sometimes use procedures from ExtList_T and List_T from within FurList_T, I need a "downcast" methods".

Instead of creating explicit options to "downcast", look into the so-called type-bound procedures, that might be really what you need:

https://software.intel.com/en-us/fortran-compiler-19.0-developer-guide-a...

https://software.intel.com/en-us/fortran-compiler-19.0-developer-guide-a...

Thank you for your reply.
Yes, I am using type-bound procedures. One example is

PROCEDURE :: get => ListGet

function, from List_T, which always returns an object upcast as a List_T.

Now, both ExtList_T and FurList_T have the "get" procedure, inherited from List_T. However, this procedure will always return an object which has been upcast as a List_T. That's why I need the downcast subroutine.

0 Kudos
FortranFan
Honored Contributor II
804 Views

holysword wrote:

..

Yes, I am using type-bound procedures. One example is

PROCEDURE :: get => ListGet

function, from List_T, which always returns an object upcast as a List_T.

Now, both ExtList_T and FurList_T have the "get" procedure, inherited from List_T. However, this procedure will always return an object which has been upcast as a List_T. That's why I need the downcast subroutine.

You can have your extension types, ExtList_T and FurList_T, either override the "get" procedure, inherited from List_T or introduce new procedures bound to their own types.  it's not clear to me why you "need the downcast subroutine"  

0 Kudos
holysword
Novice
804 Views

FortranFan wrote:

You can have your extension types, ExtList_T and FurList_T, either override the "get" procedure, inherited from List_T or introduce new procedures bound to their own types.  it's not clear to me why you "need the downcast subroutine"  

Overriding the get methods for an exact copy of their parent's method would defeat the purpose of having the classes in the first place, whose main purpose is avoid needless repetition of the code. If that's the case I'd rather just drop the whole OO altogether.

FortranFan wrote:

it's not clear to me why you "need the downcast subroutine"  

I do not blame you. I am actually working on a large code, and it is really challenging to show all the complexity of the project in a small example-application. I am, however, not asking for anyone to work on my project in my stead! I merely would like to know if what I wrote is supposed to work, if it is not supported by the Fortran standard, and if there is a way to do something analogous to the functions Cast_* that I wrote.

The dummy argument of the function Cast_ListFur receives a polymorphic List_T pointer. From my understanding, ptr_ext is a ExtList_T but *also* a List_T object, due to inheritance. Everything was declared as being polymorphic pointers. Shouldn't the compiler accept the ptr_ext as an List_T class and compile/execute? Is there any trick to get the compiler recognizing this situation properly?

0 Kudos
FortranFan
Honored Contributor II
804 Views

holysword wrote:

.. The dummy argument of the function Cast_ListFur receives a polymorphic List_T pointer. From my understanding, ptr_ext is a ExtList_T but *also* a List_T object, due to inheritance. Everything was declared as being polymorphic pointers. Shouldn't the compiler accept the ptr_ext as an List_T class and compile/execute? Is there any trick to get the compiler recognizing this situation properly?

The problem is you are (effectively) trying to create generic procedures for your "downcasting" endeavor.  As far as I recall, the Fortran standard does not support this i.e., the standard essentially requires - given its rules for type compatibility as well as argument association that for polymorphic arguments with POINTER attribute -  the actual and dummy arguments have the same declared type i.e., when CLASS(x), POINTER is involved, x is the same for the corresponding actual and dummy arguments.

So if you want to continue along the path of "downcasting", my understanding is you need to employ specific subprograms such as Cast_ListFur, Cast_ListExt, Cast_ExtFur, etc. and not expect to reuse any such utility functions of yours in other situations such as attempting to invoke Cast_ListFur to "downcast" a polymorphic FurList_T pointer to a polymorphic ExtList_T pointer.

Separately, note being able to construct and communicate a simple model of one's coding needs is a basic aspect of object-oriented methodology and design. Given your statements suggesting either an inability or unwillingness to do so, you may want to "rather just drop the whole OO altogether" or research your options further by yourself.  The references in this blog can be of great help for the latter: https://software.intel.com/en-us/blogs/2013/12/30/doctor-fortran-in-its-a-modern-fortran-world

0 Kudos
holysword
Novice
804 Views

holysword wrote:

I merely would like to know

  1.  if what I wrote is supposed to work
  2.  if it is not supported by the Fortran standard
  3.  if there is a way to do something analogous to the functions Cast_* that I wrote

FortranFan wrote:

The problem is you are (effectively) trying to create generic procedures for your "downcasting" endeavor.  As far as I recall, the Fortran standard does not support this i.e., the standard essentially requires - given its rules for type compatibility as well as argument association that for polymorphic arguments with POINTER attribute -  the actual and dummy arguments have the same declared type i.e., when CLASS(x), POINTER is involved, x is the same for the corresponding actual and dummy arguments.

So if you want to continue along the path of "downcasting", my understanding is you need to employ specific subprograms such as Cast_ListFur, Cast_ListExt, Cast_ExtFur, etc. and not expect to reuse any such utility functions of yours in other situations such as attempting to invoke Cast_ListFur to "downcast" a polymorphic FurList_T pointer to a polymorphic ExtList_T pointer.

Thank you very much for your reply and for confirming that ifort's behaviour is standard-compliant, effectively answering my questions (1) and (2) . If it is following the standard, the only thing left to do is to look for a workaround, which is my question (3).

FortranFan wrote:

So if you want to continue along the path of "downcasting", my understanding is you need to employ specific subprograms such as Cast_ListFur, Cast_ListExt, Cast_ExtFur, etc. and not expect to reuse any such utility functions of yours in other situations such as attempting to invoke Cast_ListFur to "downcast" a polymorphic FurList_T pointer to a polymorphic ExtList_T pointer.

I cannot use this workaround. It involves knowing a priori if the said pointer is a List or an ExtList before calling the Cast_* function. The role of the Cast_* function is precisely to determine that. That would defeat the whole purpose once again.

I did, however, find myself a workaround. I'm not sure if it is supposed to work like this. I basically define the dummy arguments of the Cast_* function as being TARGET instead of POINTER and everything "works". I commented the other functions and created a new Cast_AnyFur function to show that. The "works" goes in quotes, because in my actual code, I am sometimes intentionally calling those functions with null pointers. If the dummy argument of Cast_AnyFur was a POINTER, I could just test "if associated" but since it is not a pointer anymore, I can't do that. It "works", however, because apparently, inside of the SELECT TYPE in lines 28-32, none of the types match and thus "out" exits whilst still pointing to null(). I've added this test into the main program too.

  1. Is this safe?
  2. Is this the expected behaviour of the SELECT TYPE when the dummy argument has not been allocated/associated?
0 Kudos
FortranFan
Honored Contributor II
804 Views

holysword wrote:

I did, however, find myself a workaround. I'm not sure if it is supposed to work like this. ..

  1. Is this safe?
  2. Is this the expected behaviour of the SELECT TYPE when the dummy argument has not been allocated/associated?

My hunch is what you are attempting with "downcasting" is not safe, generally as well as the specific variant you're trying with the TARGET attribute.  In more ways than one, you're relying on undefined behavior.  If you think it "works" for you now in a particular processor, say Intel Fortran version x, I don't believe there's guarantee it will work with another compiler or even a future incarnation of said processor.

0 Kudos
holysword
Novice
804 Views

FortranFan wrote:

My hunch is what you are attempting with "downcasting" is not safe, generally as well as the specific variant you're trying with the TARGET attribute.  In more ways than one, you're relying on undefined behavior.  If you think it "works" for you now in a particular processor, say Intel Fortran version x, I don't believe there's guarantee it will work with another compiler or even a future incarnation of said processor.

Downcasting is a very basic operation in any OO programming language. I've asked in this very forums if modern Fortran supports this feature and the answer is "yes":

https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/621663

The question now is not "if downcasting is allowed or not", is how I can abstract it to null pointers and to multiple inheritance levels (in this case, the child of the child of the base class).

As far as being safe or unsafe, I mean according to the Fortran Standard. Does the Fortran Standard say anything about the SELECTOR of the Select Type intrinsic not being allowed to be a not associated pointer?

0 Kudos
Reply