I have a question about invoking a type bound procedure on an array. I thought I knew how to do this, but what I’m trying isn’t working. Look at this code:
implicit none
public :: tbp_t
type tbp_t
integer :: i
contains
procedure :: p_0 => print_0
!procedure :: p_1 => print_1
end type tbp_t
contains
subroutine print_0( this, s )
class(tbp_t), intent(in) :: this
character(*), intent(in) :: s
print '(a,1x,a,1x,i0)', 'print_0:', s, this%i
return
end subroutine print_0
subroutine print_1( this, s )
class(tbp_t), intent(in) :: this(:)
character(*), intent(in) :: s
integer :: p
print '(a)', 'print_1:'
do p = 1, size(this)
call this(p)%p_0( s )
enddo
return
end subroutine print_1
end module tbp_mod
program xxx
use tbp_mod
implicit none
type(tbp_t) :: a, b(3)
a%i = 42
b%i = [1,2,3]
call print_0( a, 'standard scalar' )
call a%p_0( 'tbp scalar' )
call print_1( b, 'standard array' )
!call b%p_1( 'tbp array' )
end program xxx
This small program prints a derived type in the standard way, and then prints the same thing through the type bound procedure. Then the program prints an array in the standard way. But I don’t see how to print the array with a type bound procedure. The two lines that are commented out in the above program are the ones that do not work. I cannot see how to bind the procedure (print_1() in this code) to the derived type.
This seems the most concise solution, and you can just pass a scalar CHARACTER that would be used for all elements. If you want special handling for arrays (you might want to elide elements), then something like this:
module tbp_mod
implicit none
public :: tbp_t
type tbp_t
integer :: i
contains
procedure :: p_0 => print_0
procedure :: p_1 => print_1
end type tbp_t
contains
impure elemental subroutine print_0( this, s )
class(tbp_t), intent(in) :: this
character(*), intent(in) :: s
print '(a,1x,a,1x,i0)', 'print_0:', s, this%i
return
end subroutine print_0
subroutine print_1(this, arr, s)
class(tbp_t), intent(in) :: this, arr(:)
character(*), intent(in) :: s
print '(a,1x,a,1x,*(i0,:,","))', 'print_1:', s, arr%i
end subroutine print_1
end module tbp_mod
program xxx
use tbp_mod
implicit none
type(tbp_t) :: a, b(3)
integer :: m
a%i = 42
b%i = [1,2,3]
call print_0( a, 'standard scalar' )
call print_0( b, 'standard array' )
call a%p_0( 'tbp scalar' )
call b%p_0( 'tbp array' )
call b(1)%p_1(b, 'alt')
call b(1)%p_1([(b(1),m=1,0)], 'alt')
end program xxx
Thanks for the suggestions. The impure elemental combination solves my problem.
However, it does seem odd that a more general procedure that takes an array argument cannot be a type bound procedure. My original plan was to write a scalar procedure, and an array procedure, and then bind them both to the derived type and make them generic with something like
generic :: sub => sub_scalar, sub_array
in the type definition. But if you can’t bind sub_array in the first place, then none of that works.
It all comes down to the passed-object (i.e., the hidden this, self or receiver in other languages). The standard explicitly says:
C765
The passed-object dummy argument shall be a scalar, nonpointer, nonallocatable dummy data object
with the same declared type as the type being defined; all of its length type parameters shall be assumed;
it shall be polymorphic (7.3.2.3) if and only if the type being defined is extensible (7.5.7). It shall not
have the VALUE attribute.
If you think in terms of [obj msg] (i.e., a message being dynamically dispatched to eventually be handled by an object), then it makes more sense for…
the object to be scalar: one message handled by one object, [this p_0(s)].
the procedure to be elemental:
one message handled by one object, [this p_0(s)].
multiple messages, each handled by a distinct object, [this(1:5) p_0(s(1:5))].
one message, handled simultaneously by each element in an array of objects, [this(1:5) p_0(spread(s,1,5))].
The option not covered (by OOP), and which is probably the one you want, is having an array of objects to somehow handle a single non-elemental message —in my example, it’s not clear which of the 5 elements of obj(:) would receive the message.
I think your explanation is probably correct, and it makes sense.
With normal procedure calls, you can have a subprogram that takes a scalar argument, and you can have another subprogram that takes an array argument, and you can define a generic interface and reference that interface with either a scalar argument or an array argument. The compiler looks at the argument list and finds the appropriate match at compile time. I thought that one could do the same thing with type bound procedures, including the compile time resolution of the generic interface.