Clicky

Fortran Wiki
swap (Rev #2, changes)

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

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

(削除)
! An example of an unlimited polymorphic entity used to construct a generic swap function.
! It would be nice if TRANSFER or "=" worked with unlimited polymorphic entities; and
module m
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
!===============================================================================
program p
 use m, only : swap
 blk1: block
 integer, parameter :: IP = selected_int_kind( r=2 )
 integer(IP) :: a, b
 print *, "Block 1"
 a = 2_ip
 b = 3_ip
 call swap( a, b )
 print *, "a = ", a, a==3
 print *, "b = ", b, b==2
 end block blk1
 blk2: block
 integer, parameter :: IP = selected_int_kind( r=10 )
 integer(IP) :: a, b
 print *, "Block 2"
 a = (2_ip)**42
 b = (2_ip)**43
 call swap( a, b )
 print *, "a = ", a, a==(2_ip)**43
 print *, "b = ", b, b==(2_ip)**42
 end block blk2
 blk4: block
 real :: a, b
 print *, "Block 4"
 a = -99.0
 b = 99.0
 call swap( a, b )
 print *, "a = ", a, a==99.0
 print *, "b = ", b, b==-99.0
 end block blk4
 blk5: block
 character(len=:), allocatable :: a, b
 print *, "Block 5"
 a = "foo"
 b = "bar"
 call swap( a, b )
 print *, "a = ", a, a=="bar"
 print *, "b = ", b, b=="foo"
 end block blk5
 stop
end program p
(削除ここまで)
(追記)

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(\)") 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(\) 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
 module M_swap
 implicit none
 private
 interface swap
 EOF
 for MP in $NAMES
 do
 echo " module procedure swap_$MP"
 done
 cat <<\\EOF
 end interface swap
 contains
 EOF
 }
 #############################################################
 FOOTER(){
 cat <<\\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

Revision from October 5, 2019 16:02:56 by Anonymous Coward
Forward in time (to current) | Back in time (1 more) | See current | Hide changes | History | Rollback | View: Source | Linked from: Code

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