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

Apply a procedure consistenly to all components of a derived type

AThar2
Beginner
601 Views

Hello,

 

I am trying to look for a method where I can apply a certain procedure consistently to all components of a derived type.  It can be anything where I need apply the same operations to all components of a derived type. Now, when I later on add an extra component to the derived type, I need likewise to adopt the procedures to incorporate that change.

Imagine this very simple scenario where I need to initialize all my components to a certain value.

 

subroutine reset(this,val) 
type(t_mod) :: this 
real :: val


do i = this% size_ 
   this% a(i) = val
   this% b(i) = val 
   this% c(i) = val 

enddo 

end subroutine

Another example is the routine which allocates all components. That likewise requires a similar approach as above.

 

If I later add a component "d" to my "t_mod" declared type I need manually to go to the reset subroutine and introduce the

"this% d(i) = val" ..

 

 

How can I abstract this so my procedure "reset" is executed consistently to all components of the derived type.

 In my code I have many of such subroutines which requires operations on all components. At this time I am getting frustrated that whenever I add an extra component to my t_mod", I need to go through all these routine to add that extra component.

 

Is it possible to find an abstraction to this in Fortran or mixing with c/c++

 

Thanks

 

 

 

 

 

0 Kudos
1 Solution
Steve_Lionel
Honored Contributor III
601 Views

There is no feature in Fortran to iterate through components of a derived type. You can, of course, extend a generic procedure to operate on an extended type, with the new procedure processing only the new components and calling the same generic to handle the parent, but this still requires you to write the code.

If you used an array instead of separate components, this would be easy, but I'm sure you already thought of that. (And no, different elements of an array can't be different types.)

View solution in original post

0 Kudos
4 Replies
Steve_Lionel
Honored Contributor III
602 Views

There is no feature in Fortran to iterate through components of a derived type. You can, of course, extend a generic procedure to operate on an extended type, with the new procedure processing only the new components and calling the same generic to handle the parent, but this still requires you to write the code.

If you used an array instead of separate components, this would be easy, but I'm sure you already thought of that. (And no, different elements of an array can't be different types.)

0 Kudos
AThar2
Beginner
601 Views

Hello Steve,

 

It is good to have this confirmed.

Thanks

0 Kudos
jimdempseyatthecove
Honored Contributor III
601 Views

AT90

Depending on how you externally access the components, you may be able to use a single 2D array:

this%components(componentNumber, indexWithinComponent)

then the init all becomes

this%components = val ! all components, all values
 

integer, parameter :: a=1
...
this%components(a,:)=val ! all values of component a

As to if you organize the indices the other way around, this is an optimization issue.
You may need to augment the component names slightly to avoid name collisions.

Jim Dempsey

0 Kudos
Ferdinand_T_
New Contributor II
601 Views

AT90

if your components are of different types and arrays are not a solution, you may consider a polymorphic container instead:

module m
    implicit none

    ! Element type (array valued)
    type :: Element_type
        class(*), dimension(:), allocatable :: arr
    end type

    ! Container type
    type :: Container_type
        type(Element_type), dimension(:), allocatable :: elems
    contains 
        procedure :: create => Container_create
        procedure :: reset => Container_reset
        procedure :: scale => Container_scale
        procedure :: print => Container_print
    end type

contains
 
    ! Constructor
    subroutine Container_create(this, size)
        class(Container_type), intent(out) :: this
        integer, intent(in) :: size

        ! Allocate elements
        allocate(this%elems(4))
        allocate(real    :: this%elems(1)%arr(size))
        allocate(integer :: this%elems(2)%arr(size))
        allocate(complex :: this%elems(3)%arr(size))
        allocate(real    :: this%elems(4)%arr(size))

	! ... add more elements in future ...

    end subroutine

    ! Scalar assignment
    subroutine Container_reset(this, val)
        class(Container_type), intent(inout) :: this
        real, intent(in) :: val
        integer :: i
        do i = 1, size(this%elems)
            select type (arr => this%elems(i)%arr)
            type is (integer)
                arr = floor(val)
            type is (real)
                arr = val
            type is (complex)
                arr = cmplx(val, 0.0)
            end select
        end do
    end subroutine

    ! Scalar multiplication
    subroutine Container_scale(this, val)
        class(Container_type), intent(inout) :: this
        real, intent(in) :: val
        integer :: i
        do i = 1, size(this%elems)
            select type (arr => this%elems(i)%arr)
            type is (integer)
                arr = floor(arr * val)
            type is (real)
                arr = arr * val
            type is (complex)
                arr = arr * val
            end select
        end do
    end subroutine

    ! Print to terminal
    subroutine Container_print(this)
        class(Container_type), intent(in) :: this
        integer :: i
        do i = 1, size(this%elems)
            write (*,"(a,i0,a)") 'element no.', i, ':'
            select type (arr => this%elems(i)%arr)
            type is (integer)
                write (*,"(i0)") arr
            type is (real)
                write (*,"(g0)") arr
            type is (complex)
                write (*,"('(',g0,x,g0,')')") arr
            end select
        end do
    end subroutine

    ! ... add more operations here ....

end module

program p
    use m
    implicit none 
    type(Container_type) :: myContainer

    ! Test
    call myContainer%create(10)
    call myContainer%reset(9.99)
    call myContainer%scale(0.1)
    call myContainer%print()
end program

Of course in any case the programmer has to write out the elemental operations between all the supported types of components (here: container elements), but new components can then be added with a single line in the constructor.

If one needs to mix array-valued and scalar elements etc., one could do so by making the Element_type abstract and extend to ArrayElement_type, ScalarElement_type and so on.

Kind regards
Ferdinand

0 Kudos
Reply