From: James Van Buskirk on
"relaxmike" <michael.baudin(a)gmail.com> wrote in message
news:5be1dfe2-56d4-4a32-82bf-1a0c302def89(a)k37g2000hsf.googlegroups.com...

> I still try to experiment the idea, so here is a sample
> full demonstration of the pre-processing way.
> Here is the file "test2_template.F90" :

> subroutine _READFILE_DATA_NAME ( current_data_line , myvalue ,
> status )
> implicit none
> character ( len = * ) , intent(in) :: current_data_line
> _DATATYPE , intent (out) :: myvalue
> integer, intent(out) :: status
> status = 0
> read ( current_data_line , * , err = 100 , end = 100) myvalue
> return
> 100 continue
> status = 1
> end subroutine _READFILE_DATA_NAME

> And this is the test file :
>
> module m_moduletest2
> interface readfile_data
> module procedure readfile_data_logical
> module procedure readfile_data_integer
> end interface readfile_data
> contains

> #define _DATATYPE logical
> #define _READFILE_DATA_NAME readfile_data_logical
> #include "test2_template.F90"
> #undef _DATATYPE
> #undef _READFILE_DATA_NAME

> #define _DATATYPE integer
> #define _READFILE_DATA_NAME readfile_data_integer
> #include "test2_template.F90"
> #undef _DATATYPE
> #undef _READFILE_DATA_NAME

> end module m_moduletest2

> program test2
> use m_moduletest2
> character (len=200) :: current_data_line
> integer :: myintvalue1
> logical :: mylogicalvalue1
> integer :: status
> current_data_line = "1"
> call readfile_data ( current_data_line , myintvalue1 , status )
> write ( * , * ) "status:", status
> write ( * , * ) "Integer : ", myintvalue1
> current_data_line = ".true."
> call readfile_data ( current_data_line , mylogicalvalue1 , status )
> write ( * , * ) "status:", status
> write ( * , * ) "Logical : ", mylogicalvalue1
> end program test2

OK, how about:

C:\gfortran\clf\template_war>type test3_template.i90
private
public readfile_data
interface readfile_data
module procedure READFILE_DATA_NAME
end interface readfile_data
contains
subroutine READFILE_DATA_NAME(current_data_line,Qmyvalue,status)
character(len=*), intent(in) :: current_data_line
intent (out) :: Qmyvalue
integer, intent(out) :: status
status = 0
read(current_data_line, *, err=100 , end=100) Qmyvalue
return
100 continue
status = 1
end subroutine READFILE_DATA_NAME

C:\gfortran\clf\template_war>type test3.f90
module logical_mod
implicit logical (Q)
include 'test3_template.i90'
end module logical_mod

module integer_mod
implicit integer (Q)
include 'test3_template.i90'
end module integer_mod

module m_moduletest3
use logical_mod
use integer_mod
end module m_moduletest3

program test3
use m_moduletest3
character (len=200) :: current_data_line
integer :: myintvalue1
logical :: mylogicalvalue1
integer :: status
current_data_line = "1"
call readfile_data(current_data_line, myintvalue1, status)
write(*, *) "status:", status
write(*, *) "Integer : ", myintvalue1
current_data_line = ".true."
call readfile_data(current_data_line, mylogicalvalue1, status )
write(*, *) "status:", status
write(*, *) "Logical : ", mylogicalvalue1
end program test3

C:\gfortran\clf\template_war>C:\gfortran\win64\bin\x86_64-pc-mingw32-gfortran
te
st3.f90 -otest3

C:\gfortran\clf\template_war>test3
status: 0
Integer : 1
status: 0
Logical : T

> I tried to use "include" only statements without pre-processing,
> and that lead to a source code which I am not proud of.
> But the "C void pointer" idea leads to the following source.
> The "void" idea is managed with a derived type containing all
> possible fortran basic data types.
> The "pointer" is the class name, a string containing "integer",
> or "logical", depending on the type to manage.
> This is the source code :

> module m_moduletest4
> type :: DATATYPE
> integer :: value_integer
> logical :: value_logical
> character ( len = 200 ) :: classname
> end type DATATYPE
> contains
> subroutine readfile_data ( current_data_line , classname , myvalue ,
> status )
> implicit none
> character ( len = * ) , intent(in) :: current_data_line
> character ( len = * ) , intent(in) :: classname
> type ( DATATYPE ) , intent(out) :: myvalue
> integer, intent(out) :: status
> status = 0
> myvalue % classname = classname
> select case ( classname )
> case ( "integer" )
> read ( current_data_line , * , err = 100 , end = 100) myvalue %
> value_integer
> case ( "logical" )
> read ( current_data_line , * , err = 100 , end = 100) myvalue %
> value_logical
> case default
> write(6,*) "Bad classname."
> end select
> return
> 100 continue
> status = 1
> end subroutine readfile_data
> subroutine printdata ( myvalue )
> implicit none
> type ( DATATYPE ) , intent(in) :: myvalue
> write ( * , * ) trim(myvalue % classname) , ":"
> select case ( myvalue % classname )
> case ( "integer" )
> write ( * , * ) myvalue % value_integer
> case ( "logical" )
> write ( * , * ) myvalue % value_logical
> case default
> write(6,*) "Bad classname."
> end select
> end subroutine printdata
> end module m_moduletest4

> program test4
> use m_moduletest4
> character (len=200) :: current_data_line
> type ( DATATYPE ) :: myvalue
> integer :: status
> current_data_line = "1"
> call readfile_data ( current_data_line , "integer", myvalue ,
> status )
> write ( * , * ) "status:", status
> call printdata ( myvalue )
> current_data_line = ".true."
> call readfile_data ( current_data_line , "logical" , myvalue ,
> status )
> write ( * , * ) "status:", status
> call printdata ( myvalue )
> end program test4

Isn't this an exercise in shooting fish in a barrel?

C:\gfortran\clf\template_war>type test6.f90
module m_moduletest6
use ISO_C_BINDING, only: C_PTR, C_LOC, C_F_POINTER
implicit none
private C_PTR, C_LOC, C_F_POINTER
type :: DATATYPE
type(C_PTR) value
character ( len = 200 ) :: classname
end type DATATYPE
contains
subroutine readfile_data(current_data_line,classname,myvalue,status)
implicit none
character(len=*) , intent(in) :: current_data_line
character(len=*) , intent(in) :: classname
type(DATATYPE), intent(out) :: myvalue
integer, intent(out) :: status
integer, pointer :: pi4
logical, pointer :: pL4

status = 0
myvalue%classname = classname
select case(classname)
case ("integer")
allocate(pi4)
read(current_data_line, *, err=101, end=101) pi4
myvalue%value = C_LOC(pi4)
return
101 deallocate(pi4)
case("logical")
allocate(pL4)
read(current_data_line, *, err=102, end=102) pL4
myvalue%value = C_LOC(pL4)
return
102 deallocate(pL4)
case default
write(6,*) "Bad classname."
end select
return
100 continue
status = 1
end subroutine readfile_data
subroutine printdata(myvalue)
implicit none
type(DATATYPE), intent(in) :: myvalue
integer, pointer :: pi4
logical, pointer :: pL4

write(* ,*) trim(myvalue%classname), ":"
select case(myvalue%classname)
case("integer")
call C_F_POINTER(myvalue%value, pi4)
write(* ,*) pi4
case("logical")
call C_F_POINTER(myvalue%value, pL4)
write(* ,*) pL4
case default
write(6,*) "Bad classname."
end select
end subroutine printdata
end module m_moduletest6

program test6
use m_moduletest6
character (len=200) :: current_data_line
type(DATATYPE) :: myvalue
integer :: status
current_data_line = "1"
call readfile_data(current_data_line, "integer", myvalue, status)
write(*, *) "status:", status
call printdata(myvalue)
current_data_line = ".true."
call readfile_data(current_data_line, "logical", myvalue, status)
write(* ,*) "status:", status
call printdata(myvalue)
end program test6

C:\gfortran\clf\template_war>C:\gfortran\win64\bin\x86_64-pc-mingw32-gfortran
te
st6.f90 -otest6

C:\gfortran\clf\template_war>test6
status: 0
integer:
1
status: 0
logical:
T

> - the abstract data type cannot handle user-defined derived-types.
> The pre-processing system allows to define whatever type you want to,
> without any complication in the client source code.
> This is not the case with the hand-crafted "pointer to everything"
> abstract data type.

Not the case, see my examples above.

> But you may suggest another way ?

Naturally.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


From: Charles Coldwell on
relaxmike <michael.baudin(a)gmail.com> writes:

> One example of the problems solved by C++ templates is to
> have a sorting source code which is able to manage for any
> data type, including integers, reals, or even abstract data
> types.
>
> I currently know 3 ways of dealing with templates in fortran,
> even if none of them is included in any fortran norm
> (and none of them is detailed in a fortran book, to my knowledge) :
> - pre-processing macros,
> - clever use of the "include" statement,
> - m4 macros.

You forgot "callbacks":

! Fortran 95 implementation of the quicksort algorithm. This
! subroutine does not directly touch the array it sorts; rather it
! relies on the two callbacks "compare" and "exchange" for that. Code
! inspired by R. Sedgewick, "Algorithms in C" and correspondence with
! Glen Herrmannsfeldt.

subroutine quicksort(n, compare, exchange)
implicit none
integer, intent(in) :: n ! the length of the implied array

! The compare function must return an integer that is
! greater than zero if element(i) > element(j)
! equal to zero if element(i) = element(j)
! less than zero if element(i) < element(j)
interface
integer function compare(i,j)
integer, intent(in) :: i, j
end function compare
end interface

! The exchange subroutine exchanges the elements at locations i and j.
interface
subroutine exchange(i,j)
integer, intent(in) :: i, j
end subroutine exchange
end interface

integer, dimension(2,n) :: stack
integer :: sptr, left, right, pivot

sptr = 1
call push(1, n)

do while(pop(left, right))
if (left .ge. right) cycle
pivot = partition(left, right)
if (pivot .gt. (left+right)/2) then
call push(left,pivot-1)
call push(pivot+1,right)
else
call push(pivot+1,right)
call push(left,pivot-1)
end if
end do
return

contains

subroutine push(l, r)
integer, intent(in) :: l, r

stack(1, sptr) = l
stack(2, sptr) = r
sptr = sptr + 1
end subroutine push

logical function pop(l, r)
integer, intent(out) :: l, r

if (sptr .gt. 1) then
sptr = sptr - 1
l = stack(1, sptr)
r = stack(2, sptr)
pop = .true.
else
pop = .false.
end if
end function pop

integer function partition(l,r)
integer, intent(in) :: l, r
integer :: i, j

i = l
j = r - 1
do
do while (compare(i,r) .lt. 0)
i = i+1
end do
do while (compare(j,r) .gt. 0)
if (j .eq. l) exit
j = j-1
end do
if (i .ge. j) exit
call exchange(i,j)
end do
call exchange(i,r)
partition = i
end function partition
end subroutine quicksort

Chip

--
Charles M. "Chip" Coldwell
"Turn on, log in, tune out"
GPG Key ID: 852E052F
GPG Key Fingerprint: 77E5 2B51 4907 F08A 7E92 DE80 AFA9 9A8F 852E 052F
From: relaxmike on
Thanks for these suggestions.

I must say that the method based on implicit statements is
very clever, and, based only on fortran statements, allows
to minimize the code duplication. But is it possible to
declare something like this :

module data_mod
type MYDATA
character(len=20) :: string
end type MYDATA
implicit type(MYDATA) (Q)
include 'test3_template.i90'
end module data_mod

I don't think so, which shows that the method cannot be extended
to abstract data types. But funny though.

The method based on "ISO_C_BINDING, only: C_PTR, C_LOC, C_F_POINTER"
is interesting but leads to code duplication.
If I where to use "ISO_C_BINDING", I think that I would use the
C++ templates, and only define in fortran the interface to the
C++ source code.

All in all, it would be much more simpler if the fortran language
include a "template" feature in the core, which may be done,
in 2043, may be...

Michaƫl
From: Richard Maine on
relaxmike <michael.baudin(a)gmail.com> wrote:

> I must say that the method based on implicit statements is
> very clever, and, based only on fortran statements, allows
> to minimize the code duplication. But is it possible to
> declare something like this :
>
> module data_mod
> type MYDATA
> character(len=20) :: string
> end type MYDATA
> implicit type(MYDATA) (Q)
> include 'test3_template.i90'
> end module data_mod
>
> I don't think so, which shows that the method cannot be extended
> to abstract data types.

Yes, it is possible to do something like that. The only problem with the
above is one of ordering. Implicit statements have to come quite early
in a scoping unit, before pretty much anything other than USE
statements. I forget whether it would be ok to just swap the order or
whether a different ordering constraint prevents that. If that doesn't
work, defining the derived type in a separate module, which you USE
here, would work. Derived types are allowed in implicit statements, I'm
sure.

However, I can't really recommend that approach. The well-known
error-proneness of implicit typing is greatly magnified in the presence
of derived types, modules, and host association.

--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
From: James Van Buskirk on
"relaxmike" <michael.baudin(a)gmail.com> wrote in message
news:ffd371af-6da7-44b4-b431-d2e189af5978(a)c19g2000prf.googlegroups.com...

> I must say that the method based on implicit statements is
> very clever, and, based only on fortran statements, allows
> to minimize the code duplication. But is it possible to
> declare something like this :

> module data_mod
> type MYDATA
> character(len=20) :: string
> end type MYDATA
> implicit type(MYDATA) (Q)
> include 'test3_template.i90'
> end module data_mod

> I don't think so, which shows that the method cannot be extended
> to abstract data types. But funny though.

The method is indeed clever. Wish I had thought of it myself,
but someone else suggested it in clf.

As Richard pointed out of course you can do this:

C:\gfortran\clf\template_war>type test3_template.i90
private
public readfile_data
interface readfile_data
module procedure READFILE_DATA_NAME
end interface readfile_data
contains
subroutine READFILE_DATA_NAME(current_data_line,Qmyvalue,status)
character(len=*), intent(in) :: current_data_line
intent (out) :: Qmyvalue
integer, intent(out) :: status
status = 0
read(current_data_line, *, err=100 , end=100) Qmyvalue
return
100 continue
status = 1
end subroutine READFILE_DATA_NAME

C:\gfortran\clf\template_war>type test7.f90
module data_mod
implicit none
type MYDATA
character(len=20) string
end type MYDATA
end module data_mod

module logical_mod
implicit logical (Q)
include 'test3_template.i90'
end module logical_mod

module integer_mod
implicit integer (Q)
include 'test3_template.i90'
end module integer_mod

module MYDATA_mod
use data_mod
implicit type(MYDATA) (Q)
include 'test3_template.i90'
end module MYDATA_mod

module m_moduletest3
use logical_mod
use integer_mod
use MYDATA_mod
end module m_moduletest3

program test3
use data_mod
use m_moduletest3
character (len=200) :: current_data_line
integer :: myintvalue1
logical :: mylogicalvalue1
type(MYDATA) :: myMYDATAvalue1
integer :: status
current_data_line = "1"
call readfile_data(current_data_line, myintvalue1, status)
write(*, *) "status:", status
write(*, *) "Integer : ", myintvalue1
current_data_line = ".true."
call readfile_data(current_data_line, mylogicalvalue1, status )
write(*, *) "status:", status
write(*, *) "Logical : ", mylogicalvalue1
current_data_line = "'Hello from James'"
call readfile_data(current_data_line, myMYDATAvalue1, status )
write(*, *) "status:", status
write(*, *) "MYDATA : ", myMYDATAvalue1
end program test3

C:\gfortran\clf\template_war>c:\gfortran\win64\bin\x86_64-pc-mingw32-gfortran
te
st7.f90 -otest7

C:\gfortran\clf\template_war>test7
status: 0
Integer : 1
status: 0
Logical : T
status: 0
MYDATA : Hello from James

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end