Clicky

Fortran Wiki
swap (changes)

Skip the Navigation Links | Home Page | All Pages | Recently Revised | Authors | Feeds | Export |

Showing changes from revision #2 to #3: (追記) Added (追記ここまで) | (削除) Removed (削除ここまで) | (削除) Chan (削除ここまで)(追記) ged (追記ここまで)

Making a generic routine without built-in template support in Fortran


Fortran does not have explicit facilities for building generic templates (for creating generic routines without duplicating a lot of code).

INCLUDE files, the ISO_C_BINDING interface (to get to move_alloc, memcpy, and other C pointer functionality), source(), transfer(), and unlimited polymorphic entities (ie.(削除) "CLASS(\ (削除ここまで)(追記) "CLASS(*)") (追記ここまで)(追記) are (追記ここまで)(追記) a (追記ここまで)(追記) poor (追記ここまで)(追記) substitute (追記ここまで)(追記) but (追記ここまで)(追記) can (追記ここまで)(追記) sometimes (追記ここまで)(追記) help. (追記ここまで)(削除) )") are a poor substitute but can sometimes help. (削除ここまで)

INCLUDE files can be very useful for reducing duplicate code but have the disadvantage of breaking the code into seperate files. There is no Fortran feature to allow the common code to reside in a single file along with the parameterized code.

Note that(削除) CLASS(\ (削除ここまで)(追記) CLASS(*) (追記ここまで)(追記) basically (追記ここまで)(追記) allows (追記ここまで)(追記) runtime (追記ここまで)(追記) type-safe (追記ここまで)(追記) but (追記ここまで)(追記) type-agnostic (追記ここまで)(追記) storage, (追記ここまで)(追記) put (追記ここまで)(追記) can (追記ここまで)(追記) sometimes (追記ここまで)(追記) be (追記ここまで)(追記) used (追記ここまで)(追記) to (追記ここまで)(追記) substitute (追記ここまで)(追記) as (追記ここまで)(追記) a (追記ここまで)(追記) compile-time (追記ここまで)(追記) type (追記ここまで)(追記) parameterisation (追記ここまで)(追記) mechanism. (追記ここまで)(削除) ) basically allows runtime type-safe but type-agnostic storage, put can sometimes be used to substitute as a compile-time type parameterisation mechanism. (削除ここまで)

Preprocessors such as m4(), cpp(), ... can be very useful. An often overlooked but simple option is to write your code as "here" documents in the sh(1) or bash(1) shell. Combined with adding a make(1) rule for building ".sh" file suffixes this is a very flexible and readily available option.

Generic Interfaces

So you can hide the ugly details from the user with generic interfaces, but this requires a routine for each combination of parameter types. Even for the simple idea of swapping two variables of the same type (A very common operation in sorting algorithms) you can see a lot of duplicate code when building a generic interface:

Module M_swap
! SWAP is a Generic Interface in a module with PRIVATE specific procedures.
! that swaps two variables of like type (real,integer,complex,character,double)
! you must add a new routine for each new type directly supported; although 
! multiple calls can be used on the components of a non-intrinsic type.
 implicit none
 integer,parameter :: cd=kind(0.0d0)
 private
 public swap
 interface swap
 module procedure r_swap, i_swap, c_swap, s_swap, d_swap, l_swap, cd_swap
 end interface
contains
 elemental subroutine d_swap(lhs,rhs)
 doubleprecision, intent(inout) :: lhs,rhs
 doubleprecision :: temp
 temp = lhs; lhs = rhs; rhs = temp
 end subroutine d_swap
 elemental subroutine r_swap(lhs,rhs)
 real, intent(inout) :: lhs,rhs
 real :: temp
 temp = lhs; lhs = rhs; rhs = temp
 end subroutine r_swap
 elemental subroutine i_swap(lhs,rhs)
 integer, intent(inout) :: lhs,rhs
 integer :: temp
 temp = lhs; lhs = rhs; rhs = temp
 end subroutine i_swap
 elemental subroutine l_swap(l,ll)
 logical, intent(inout) :: l,ll
 logical :: ltemp
 ltemp = l; l = ll; ll = ltemp
 end subroutine l_swap
 elemental subroutine c_swap(lhs,rhs)
 complex, intent(inout) :: lhs,rhs
 complex :: temp
 temp = lhs; lhs = rhs; rhs = temp
 end subroutine c_swap
 elemental subroutine cd_swap(lhs,rhs)
 complex(kind=cd), intent(inout) :: lhs,rhs
 complex(kind=cd) :: temp
 temp = lhs; lhs = rhs; rhs = temp
 end subroutine cd_swap
 elemental subroutine s_swap(string1,string2)
 character(len=*), intent(inout) :: string1,string2
 character( len=max(len(string1),len(string2))) :: string_temp
 string_temp = string1; string1 = string2; string2 = string_temp
 end subroutine s_swap
end module M_swap

Unlimited polymorphic variables

When the operations generally just require moving or changing the storage, functions like TRANSFER() and unlimited polymorphic entities can be useful for reducing duplicate code. This is often the case for sorting, swapping, and various types of lists.

So another example using an unlimited polymorphic entity to construct a generic swap function follows.

It would be nice if TRANSFER or "=" worked with unlimited polymorphic entities ; so you have to add a type into ANYTHING_TO_BYTES() an BYTES_TO_ANYTHING) and this approach precludes making the routine elemental


module m_swap
use, intrinsic :: iso_fortran_env, only : int8,int16,int32,int64,real32,real64,real128
implicit none
private
public :: swap
integer,parameter :: dble=kind(0.0d0)
interface swap
 module procedure swap_scalar
end interface
contains
function anything_to_bytes(anything) result(chars)
 implicit none
 class(*),intent(in) :: anything
 character(len=1),allocatable :: chars(:)
 select type(anything)
 type is (character(len=*)); chars=transfer(anything,chars)
 type is (complex); chars=transfer(anything,chars)
 type is (complex(kind=dble)); chars=transfer(anything,chars)
 type is (integer(kind=int8)); chars=transfer(anything,chars)
 type is (integer(kind=int16)); chars=transfer(anything,chars)
 type is (integer(kind=int32)); chars=transfer(anything,chars)
 type is (integer(kind=int64)); chars=transfer(anything,chars)
 type is (real(kind=real32)); chars=transfer(anything,chars)
 type is (real(kind=real64)); chars=transfer(anything,chars)
 type is (real(kind=real128)); chars=transfer(anything,chars)
 class default
 stop 'crud. anything_to_bytes(1) does not know about this type'
 end select
end function anything_to_bytes
subroutine bytes_to_anything(chars,anything)
 character(len=1),allocatable :: chars(:)
 class(*) :: anything
 select type(anything)
 type is (character(len=*)); anything=transfer(chars,anything)
 type is (complex); anything=transfer(chars,anything)
 type is (complex(kind=dble)); anything=transfer(chars,anything)
 type is (integer(kind=int8)); anything=transfer(chars,anything)
 type is (integer(kind=int16)); anything=transfer(chars,anything)
 type is (integer(kind=int32)); anything=transfer(chars,anything)
 type is (integer(kind=int64)); anything=transfer(chars,anything)
 type is (real(kind=real32)); anything=transfer(chars,anything)
 type is (real(kind=real64)); anything=transfer(chars,anything)
 type is (real(kind=real128)); anything=transfer(chars,anything)
 class default
 stop 'crud. bytes_to_anything(1) does not know about this type'
 end select
end subroutine bytes_to_anything
subroutine swap_scalar( lhs, rhs )
 class(*) :: rhs
 class(*) :: lhs
 character(len=1),allocatable :: templ(:)
 character(len=1),allocatable :: tempr(:)
 tempr=anything_to_bytes(rhs)
 templ=anything_to_bytes(lhs)
 call bytes_to_anything(templ,rhs)
 call bytes_to_anything(tempr,lhs)
end subroutine swap_scalar
end module m_swap

Unlimited polymorphic variables and ISO_C_BINDING access to low-level C functionality.

Sometimes using the ISO_C_BINDING interface to use lower-level functions supported in C (and to therefore open up the code to some of risks of working with addresses) combined with polymorphic variables can be useful.

WARNING: This did not work with CHARACTER variables using the compiler I tested with. In theory I think this should work with any type. Corrections are welcome.

module M_swap
private
public swap
contains
 subroutine swap(lhs,rhs)
 use iso_c_binding
 implicit none
 class(*),intent(inout) :: lhs, rhs
 class(*), allocatable :: temp
 type(c_ptr) :: tmp
! Copy N bytes of SRC to DEST, no aliasing or overlapping allowed.
! extern void *memcpy (void *dest, const void *src, size_t n);
 interface
 subroutine s_memcpy(dest, src, n) bind(C,name='memcpy')
 use iso_c_binding
 INTEGER(c_intptr_t), value, intent(in) :: dest
 INTEGER(c_intptr_t), value, intent(in) :: src
 integer(c_size_t), value :: n
 end subroutine s_memcpy
 end interface
 temp=lhs
 call s_memcpy(loc(lhs), loc(rhs), storage_size(lhs, kind=c_size_t)/8_c_size_t )
 call s_memcpy(loc(rhs), loc(temp), storage_size(rhs, kind=c_size_t)/8_c_size_t )
 end subroutine swap
end module M_swap

A nod towards preprocessors

The output of this bash shell is a generic interface. This approach is a lot more useful when more complex code sections are indentical but is far more flexible than INCLUDE which does not allow for looping or substition or even cpp(1). cpp(1) has macros and substitution but also does not support functions and looping. More powerful preprocessors like m4(1) do, but bash(1) is more commonly available and have a larger user base.

 #!/bin/bash
 #############################################################
 GENERIC(){
 TYPE=1ドル
 cat <<EOF
 
 subroutine swap_${TYPE}(a,b)
 $TYPE :: a,b,temp
 temp=a
 a=b
 b=a
 end subroutine swap_$TYPE
 EOF
 }
 #############################################################
 HEADER(){
 # module header
 cat(削除) <<\\EOF
 (削除ここまで)(追記) <<\EOF
 (追記ここまで) module M_swap
 implicit none
 private
 interface swap
 EOF
 for MP in $NAMES
 do
 echo " module procedure swap_$MP"
 done
 cat(削除) <<\\EOF
 (削除ここまで)(追記) <<\EOF
 (追記ここまで) end interface swap
 contains
 EOF
 }
 #############################################################
 FOOTER(){
 cat(削除) <<\\EOF
 (削除ここまで)(追記) <<\EOF
 (追記ここまで) end module M_swap
 EOF
 }
 #############################################################
 # add types here
 NAMES='integer real doubleprecision complex'
 HEADER
 for NAME in $NAMES
 do
 GENERIC $NAME
 done
 FOOTER
 exit

category: code

Revised on March 3, 2023 08:22:35 by urbanjost (73.40.218.30) (9902 characters / 4.0 pages)
Edit | Back in time (2 revisions) | Hide changes | History | Views: Print | TeX | Source | Linked from: Code

AltStyle によって変換されたページ (->オリジナル) /