- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
I wonder how to write overriding operator when extending an abstract type. One know example writes the interface but not the details of the subroutine. It seems that the standard force the extended add function to have the same type. and then in add_my_integer , argument b should be of class my_numeric_type. And thus, we are force to write a select_type to add two integers. Isn't that just forcing the programmer to define the overriding feature ? Is there a reason to have that design in the standard ?
module test
type, abstract :: my_numeric_type
contains
private
procedure(op2), deferred :: add
procedure(op2), deferred :: subtract
! procedures for other operations not shown
generic, public :: operator(+) => add
generic, public :: operator(-) => subtract
! generic specs for other operations not shown
end type my_numeric_type
abstract interface
function op2(a, b) result(r)
import :: my_numeric_type
class(my_numeric_type), intent(in) :: a, b
class(my_numeric_type), allocatable :: r
end function op2
end interface
type, extends(my_numeric_type) :: my_integer
integer, private :: value
contains
procedure :: add => add_my_integer
procedure :: subtract => subtract_my_integer
end type my_integer
contains
function add_my_integer(a,b) result(r)
class(my_integer),intent(in) :: a
class(my_numeric_type),intent(in) :: b
class(my_numeric_type),allocatable :: r
allocate(my_integer::r)
r%value=a%value+b%value
end function
function subtract_my_integer(a,b) result(r)
class(my_integer),intent(in) :: a
class(my_numeric_type),intent(in) :: b
class(my_numeric_type),allocatable :: r
allocate(my_integer::r)
r%value=a%value-+b%value
end function
end module test
Another thing is the ability to overload procedure like in
type mycomplex private contains procedure :: mycomplex_plus_mycomplex procedure:: mycomplex_plus_real procedure, pass(b) :: real_plus_mycomplex generic:: operator(+) => mycomplex_plus_mycomplex, & mycomplex_plus_real, real_plus_mycomplex end type mycomplex
How does the multiple procedure for operator(+) works if operator(+) is defined in abstract, and the standard force everything to be class my_numeric_type. Then the automatic overriding feature based on the type of the parameter can not work ?
Thanks.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A full example with what i understand should work is :
module num
type, abstract :: my_numeric_type
contains
private
procedure(op2), deferred :: add
generic, public :: operator(+) => add
procedure(op), deferred :: ass
generic, public :: assignment(=) => ass
end type
abstract interface
subroutine op(a,b)
import :: my_numeric_type
class(my_numeric_type), intent(out) :: a
class(my_numeric_type), intent(in) :: b
end subroutine op
function op2(a,b) result (r)
import :: my_numeric_type
class(my_numeric_type), intent(in) :: a,b
class(my_numeric_type), allocatable :: r
end function op2
end interface
type, extends(my_numeric_type) :: my_integer
integer, public :: value
contains
procedure :: add => add_my_integer
procedure :: ass => ass_my_integer
generic, public :: operator(+) => add,add_2_int,add_int_real
end type
contains
function add_2_int(a,b) result(r)
class(my_integer), intent(in) :: a
class(my_integer), intent(in) :: b
class(my_integer), allocatable :: r
r%value = a%value+b%value
end function
function add_int_real(a,b) result(r)
class(my_integer), intent(in) :: a
real, intent(in) :: b
real, allocatable :: r
r = a%value+b
end function
function add_my_integer(a,b) result(r)
class(my_integer), intent(in) :: a
class(my_numeric_type), intent(in) :: b
class(my_numeric_type), allocatable :: r
select type (b)
type is (my_integer)
allocate(my_integer :: r)
select type (r)
type is (my_integer)
r%value = a%value+b%value
end select
end select
end function
subroutine ass_my_integer(a,b)
class(my_integer), intent(out) :: a
class(my_numeric_type), intent(in) :: b
select type (b)
type is (my_integer)
a%value = b%value
end select
end subroutine
end module
program main
use num
class(my_integer), allocatable :: a, b, c
allocate(my_integer :: a)
allocate(my_integer :: b)
allocate(my_integer :: c)
a=my_integer(1)
b=my_integer(2)
c = a+b
write (*,*) c%value
end program
But then I got those error :
testabstractgeneric2.f90(31): error #8423: In GENERIC type bound procedure definition each binding name must be the name of a specific binding of the type. [ADD_2_INT]
generic, public :: operator(+) => add,add_2_int,add_int_real
--------------------------------------------^
testabstractgeneric2.f90(31): error #8423: In GENERIC type bound procedure definition each binding name must be the name of a specific binding of the type. [ADD_INT_REAL]
generic, public :: operator(+) => add,add_2_int,add_int_real
------------------------------------------------------^
compilation aborted for testabstractgeneric2.f90 (code 1)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The language doesn't permit you to do what you want to do directly - the passed argument of a procedure nominated by a specific binding varies according to the type that overrides the specific binding, but all other arguments must match in terms of type and other characteristics. So in terms of your first post - yes - you pretty much have to use select type on one of the arguments.
If things weren't this way then generic procedures couldn't be resolved at compile time. The dynamic type of an object is a run time property. Consider what happens if you had my_integer and my_real that were both extensions of my_numeric type. You can do something like:
CLASS(my_numeric_type), ALLOCATABLE :: a CLASS(my_numeric_type), ALLOCATABLE :: b CLASS(my_numeric_type), ALLOCATABLE :: c ... ALLOCATE(my_integer :: a) ALLOCATE(my_real :: b) ... ALLOCATE(c, SOURCE=a+b)
The declared type of a and b is my_numeric_type. The deferred specific binding that is behind the generic interface for (+) says (+) takes a passed argument that extends my_numeric_type and another argument of my_numeric_type. The expression after the source fits that pattern, so the compiler can resolve the generic binding to a specific binding. If it was acceptable for the procedure that implements the override of the add specific binding in my_integer to take class(my_integer) for the right hand argument you then have a runtime type mismatch.
In terms of your second post... generic bindings involve a set of specific bindings. You have you generic bindings referencing stand-alone procedures. You "want" add_2_int and add_int_real to be behind specific bindings of my_integer. The problem you are then going to run into though is that you then have ambiguous generics - add_2_int and add_my_integer cannot be distinguished and cannot both sit behind the same generic (given a right hand argument of type my_integer the compiler doesn't know which specific binding the generic resolves to). Unlike some other languages Fortran is very clear about how generic references map through to specific procedures or bindings in terms of type and kind... it doesn't have a concept that one set of argument types might somehow match better than another set - any sort of ambiguity is an error.
(In terms of rank matching there is a priority of non-elemental specifics over elemental specifics.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Patrice l. wrote:
... And thus, we are force to write a select_type to add two integers. Isn't that just forcing the programmer to define the overriding feature ? Is there a reason to have that design in the standard ?
..
IanH wrote:
..
So in terms of your first post - yes - you pretty much have to use select type on one of the arguments.
..
Patrice I.,
For my own benefit and learning, are there some concerns or issues with SELECT TYPE construct that you know of and which you can share here? From this post as well as your other post https://software.intel.com/en-us/forums/topic/533445, you seem to suggest some problems with the use of this construct.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
FortranFan,
As a fan of fortran, I like performance and I am trying to write OO fortran in a way that avoid loosing to much with polymorphism,
and stay close to a 'classical' fortran program. I just don't like unecessary check (if) to do basic operations. In this example, OO might be beautiful to define object that has numeric type and have the ability to change it (real,int,complex) without having to write different subroutines. But what is the cost ? adding two integer cost 2 select type + one allocate + the actual addition . You see where i am going if you have to do that 10 million times. Unless i am wrong and the compiler is super smart and reduce that to a classical r=a+b .
This example is only considering non-allocatable intrisic component, when your code start to be complicated, with type including allocatable array of other type with 2 or 3 levels. Ff each time you do an assignment you have to select type plus reallocate , and all of that to do in the end toto%tata%titi%real=toto%tata%titi%real, then you spend a fair amount of time managing object and not really doing computation. Again unless i have a misconception of how the compiler will optimize all those encapsulation layers.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Patrice l. wrote:
FortranFan,
As a fan of fortran, I like performance and I am trying to write OO fortran in a way that avoid loosing to much with polymorphism,
and stay close to a 'classical' fortran program. I just don't like unecessary check (if) to do basic operations. In this example, OO might be beautiful to define object that has numeric type and have the ability to change it (real,int,complex) without having to write different subroutines. But what is the cost ? adding two integer cost 2 select type + one allocate + the actual addition . You see where i am going if you have to do that 10 million times. Unless i am wrong and the compiler is super smart and reduce that to a classical r=a+b .
This example is only considering non-allocatable intrisic component, when your code start to be complicated, with type including allocatable array of other type with 2 or 3 levels. Ff each time you do an assignment you have to select type plus reallocate , and all of that to do in the end toto%tata%titi%real=toto%tata%titi%real, then you spend a fair amount of time managing object and not really doing computation. Again unless i have a misconception of how the compiler will optimize all those encapsulation layers.
Thanks much, I'm now relieved. As you know, real drivers and considerable thought are needed for OO design and programming. In our experience, when both these aspects are realized, the benefits of OO far exceed traditional, procedural programming. For example, without going into details, a quadrature programming library can become extensible, thread-safe/parallelizable, scalable, and thus more computationally efficient overall with OO, not to mention other possible benefits with support and maintenance. But if the focus is simply on performance of function evaluations, then OO is not worth the effort.
Separately, in Fortran, polymorphic variables and SELECT TYPE do go hand in hand. Whether to use polymorphic variables is a matter of much, much higher level of thought in terms of code design involving OO principles; once that is decided, I don't think coders need to "penny-pinch" on SELECT TYPE.
Adios,
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page