On Tuesday, September 24, 2019 at 1:36:13 PM UTC-4, GianLuigi Piacentini wrote:
> ..
> Fortranfan,
>
> your solution, is probably the way to go, to save the possibility to use
> operators. I'm just wandering what's the difference, in practice, with
> respect to the old (say Fortran95) way to do this same thing - having
> explicitly a my_type instance in new_type. Seem to me type extension
> does not have any advantage in this case ...
> I will anyway save your example and peruse it.
>
> Many thanks
> Gigi
Re: "Seem to me type extension does not have any advantage in this case," as shown above with a minimal example with 2 'classes' that have no meaningful data and no methods which do anything interesting on the data, yes there's really no benefit with type extension. One has to do object-oriented analysis and design (OOA and OOD) and really find good reasons to employ inheritance and only then will the benefits with type extension become obvious.
Note now, should one have sufficient rationale to use 'class' inheritance and want to pursue Java (or C++ with class methods, or C#/Visual Basic) style coding with *type-bound procedures* (TBPs), here's an alternative you may want to review: notice the aspects about extension of the generic interface with the defined operator (+) and procedure overriding with 'output':
--- begin example 2 ---
module my_type_old
implicit none
private
type, public :: my_type
private
real :: r = 1.0
contains
private !<-- default, typically a good OO coding practice
procedure, pass(lhs) :: sum_my_types
procedure, pass(this), public :: output => output_my_types
generic, public :: operator(+) => sum_my_types
end type my_type
contains
function sum_my_types (lhs, rhs) result (t_sum)
! Argument list
class(my_type), intent(in) :: lhs
type(my_type), intent(in) :: rhs
type(my_type) :: t_sum
t_sum%r = lhs%r + rhs%r
return
end function sum_my_types
subroutine output_my_types( this )
class(my_type), intent(in) :: this
print *, "t%r = ", this%r
end subroutine
end module my_type_old
module extended_type
use my_type_old
implicit none
private
type, extends(my_type), public :: new_type
private
integer :: i = 21
contains
private !<-- default, a good OO coding practice
procedure, pass(lhs) :: sum_new_types
procedure, pass(this), public :: output => output_new_types !<-- Procedure override
generic, public :: operator(+) => sum_new_types !<-- extend the generic interface for the defined operation
end type new_type
contains
function sum_new_types (lhs, rhs) result (t_sum)
! Argument list
class(new_type), intent(in) :: lhs
type(new_type), intent(in) :: rhs
type(new_type) :: t_sum
t_sum%my_type = lhs%my_type + rhs%my_type
t_sum%i = lhs%i + rhs%i
return
end function sum_new_types
subroutine output_new_types( this )
class(new_type), intent(in) :: this
call this%my_type%output()
print *, "t%i = ", this%i
end subroutine
end module extended_type
program p
blk1: block
use my_type_old, only : my_type
type(my_type) :: foo, bar
type(my_type) :: foobar
print *, "Block 1"
foobar = foo + bar
call foobar%output()
print *, "Expected is r = 2.0"
end block blk1
print *
blk2: block
use extended_type, only : new_type
type(new_type) :: foo, bar
type(new_type) :: foobar
print *, "Block 2"
foobar = foo + bar
call foobar%output()
print *, "Expected is r = 2.0"
print *, "and i = 42"
end block blk2
print *
blk3: block
use my_type_old, only : my_type
use extended_type, only : new_type
type(new_type) :: foo, bar
class(my_type), allocatable :: foobar
print *, "Block 3"
!foobar = foo + bar !<-- No allocate on assignment with TBP :-(
allocate( new_type :: foobar )
foobar = foo + bar
call foobar%output()
print *, "Expected is r = 2.0"
print *, "and i = 42"
end block blk3
print *
blk4: block
use my_type_old, only : my_type
class(my_type), allocatable :: foo, bar
class(my_type), allocatable :: foobar
print *, "Block 4"
allocate( my_type :: foo, bar, foobar )
foobar = foo + bar !<-- No allocate on assignment with TBP :-(
call foobar%output()
print *, "Expected is r = 2.0"
end block blk4
print *
blk5: block
use my_type_old, only : my_type
use extended_type, only : new_type
class(my_type), allocatable :: foo, bar
class(my_type), allocatable :: foobar
print *, "Block 5"
allocate( new_type :: foo, bar, foobar )
foobar = foo + bar !<-- No allocate on assignment with TBP :-(
call foobar%output()
print *, "Expected is r = 2.0"
print *, "and i = 42"
end block blk5
print *
blk6: block
use my_type_old, only : my_type
use extended_type, only : new_type
class(my_type), allocatable :: foo, bar
type(new_type) :: foobar
print *, "Block 6"
allocate( new_type :: foo, bar )
select type ( foo )
type is ( new_type )
select type ( bar )
type is ( new_type )
foobar = foo + bar !<-- Assignment inside verbose 'casting'
end select
end select
call foobar%output()
print *, "Expected is r = 2.0"
print *, "and i = 42"
end block blk6
print *
blk7: block
use extended_type, only : new_type
type(new_type) :: foo, bar
print *, "Block 7"
associate ( foobar => foo+bar ) !<-- not supported by gfortran
call foobar%output()
print *, "Expected is r = 2.0"
print *, "and i = 42"
end associate
end block blk7
end program
--- end example 2 ---
Upon execution with Intel Fortran, the output is as I expect:
Block 1
t%r = 2.000000
Expected is r = 2.0
Block 2
t%r = 2.000000
t%i = 42
Expected is r = 2.0
and i = 42
Block 3
t%r = 2.000000
t%i = 42
Expected is r = 2.0
and i = 42
Block 4
t%r = 2.000000
Expected is r = 2.0
Block 5
t%r = 2.000000
Expected is r = 2.0
and i = 42
Block 6
t%r = 2.000000
t%i = 42
Expected is r = 2.0
and i = 42
Block 7
t%r = 2.000000
t%i = 42
Expected is r = 2.0
and i = 42
whereas gfortran does not support the ASSOCIATE construct in "Block 7" involving the defined operation; my hunch is that is a gfortran bug, though I'm not completely sure about it, I will need to review the standard more closely.