Reputation: 207
Using code from the Fortran90 standard, is there a way to pass the name of a derived type element to a subroutine in Fortran? I'd like to do something like the following:
TYPE :: DataContainer
REAL :: DataElementA
REAL :: DataElementB
REAL :: DataElementC
END TYPE DataContainer
SUBROUTINE ComplexOperation(DataMatrixParameter, DataElementName, Parameter)
! Parameter Typing for DataElementName?
DataMatrixParameter%DataElementName = Parameter
END SUBROUTINE
TYPE (DataContainer), DIMENSION :: Data
CALL ComplexMatrixOperation(Data, DataElementA, 5)
CALL ComplexMatrixOperation(Data, DataElementC, 4)
So that Data%DataElementA
is 5
and Data%DataElementC
is 4
. The DataElementName parameter could be any kind of element identifier, but I would prefer to avoid strings and case statements. Those familiar with C++ will recognize it as something directly analogous to the pointer to member feature.
My real problem is, of course, much more complex than just assigning the elements to a given value.
Upvotes: 2
Views: 859
Reputation: 7395
Although it is probably not possible to mimic the C++ way for using pointers to class members and also there is no native metaprogramming facility at present (except for preprocessor macros), Fortran instead has the ability to create arrays of type components efficiently without making any array temporaries (as suggested above), which might be useful for the OP's purpose. To illustrate this, here is some example code for using such component arrays:
module testmod
implicit none
type A_t
integer :: u = 0, v = 0
endtype
type B_t
integer :: x = 0, y = 0
type(A_t) :: a
endtype
contains
subroutine setval ( elem, val )
integer :: elem(:), val !! assumed-shape array
elem(:) = val
endsubroutine
subroutine setval_2D ( elem, val )
integer :: elem(:,:), val !! assumed-shape array
elem(:,:) = val
endsubroutine
subroutine setval_explicit ( elem, n, val )
integer :: n, elem( n ), val !! explicit-shape array
elem( 1:n ) = val
endsubroutine
end module
program main
use testmod
implicit none
type(B_t), target :: b( 2 ), bmat( 2, 2 )
!! Pass 1D component arrays.
call setval ( b(:)% x, 1 )
call setval ( b(:)% y, 2 )
call setval ( b(:)% a% u, 3 )
call setval ( b(:)% a% v, 4 )
print *, "b( : )% x = ", b( : )% x
print *, "b( : )% y = ", b( : )% y
print *, "b( : )% a% u = ", b( : )% a% u
print *, "b( : )% a% v = ", b( : )% a% v
print *, "b(1) = ", b(1)
print *, "b(2) = ", b(2)
!! Pass a 2D component array.
call setval_2D ( bmat(:,:)% x, 50 )
print *, "bmat(:,:)% x = ", bmat(:,:)% x
!! Pass 1D component array sections.
call setval ( bmat(:, 1)% x, 1 )
call setval ( bmat(:, 2)% x, 2 )
call setval ( bmat(2, :)% x, 5 )
print *, "bmat(:,:)% x = ", bmat(:,:)% x
!! Pass a 2D component array to an explicit-shape dummy array
!! (in this case, copy-in/copy-out may occur).
call setval_explicit ( bmat(:,:)% x, size(bmat), 100 )
print *, "bmat(:,:)% x = ", bmat(:,:)% x
endprogram
with the result
b( : )% x = 1 1
b( : )% y = 2 2
b( : )% a% u = 3 3
b( : )% a% v = 4 4
b(1) = 1 2 3 4
b(2) = 1 2 3 4
bmat(:,:)% x = 50 50 50 50
bmat(:,:)% x = 1 5 2 5
bmat(:,:)% x = 100 100 100 100
[Side note] It is also interesting to see how metaprogramming works in dynamic languages, e.g. with Julia (a similar thing might be possible with preprocessor macros, though).
function test( a, fieldname, val )
@eval $a.$fieldname = $val
end
type Person
age :: Int
weight :: Float64
end
foo = Person( 0, 0.0 )
test( foo, :age, 100 )
test( foo, :weight, 789.0 )
@show foo.age
@show foo.weight
Upvotes: 0
Reputation: 32366
From what I can gather you want to be able to write something like
call ComplexMatrixOperation(Data, DataElementA, ...)
where Data
is a matrix, so that a subroutine looking something like
subroutine ComplexMatrixOperation(matrix, selector, ...)
type(DataContainer) matrix(:,:)
something_magical selector
can act on the elements matrix(i,j)%DataElementA
or matrix(i,j)%DataElementC
depending on the selector.
That something_magical
doesn't exist. However, you still have options (beyond those already answered).
As we can see that DataElementA
is a scalar component we are able to reference Data%DataElementA
which is an array of reals of shape that of Data
.
You can, then,
call ComplexMatrixOperation(Data%DataElementA, ...)
or
call ComplexMatrixOperation(Data%DataElementC, ...)
for
subroutine ComplexMatrixOperation(matrix, ...)
real matrix(:,:)
if we again assume that Data
is of rank 2 (adjust accordingly).
Upvotes: 1
Reputation: 29244
You can't do this. Your best bet is to use an array instead of named elements
TYPE :: DataContainer
REAL, DIMENSION(3) :: DataElement
END TYPE DataContainer
and then use it with
SUBROUTINE ComplexOperation(DataMatrixParameter, DataElementIndex, Parameter)
TYPE(DataContainer) :: DataMatrixParameter
INTEGER, INTENT(IN) :: DataElementIndex
REAL, INTENT(IN) :: Parameter
DataMatrixParameter%DataElement(DataElementIndex) = Parameter
END SUBROUTINE
Maybe use named constants like
INTEGER, PARAMETER :: DataElementA=1, DataElementB=2, DataElementC=3
to be used as
CALL ComplexOperation(Data, DataElementC, 1.33)
Upvotes: 0