Fortran templates

639 views
Skip to first unread message

relaxmike

unread,
Apr 15, 2008, 10:16:41 AM4/15/08
to
Hi all fortran gurus !

I would like to discuss the current methods to manage
templates in fortran, following the thread

http://groups.google.com/group/comp.lang.fortran/browse_thread/thread/f055f870774d98a0/110fa05197d7b709?lnk=gst&q=templates+fortran#110fa05197d7b709

which was very interesting, but showed no details on
how to pratically manage the source code.
This feature is very well-known by C++ developers as "templates".

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.

Let's begin with the pre-processing macros.
Suppose that the file "sorting_template.f90" contains a template
sorting module, parametrized by the _QSORT_TYPE macro.
The following is an example of parametrized argument declaration :

subroutine qsort ( array, compare )
_QSORT_TYPE, dimension(:) :: array

Here "_QSORT_TYPE" may be an integer, a real or any fortran
derived type.
This name has been chosen because a fortran variable cannot
begin with an underscore so that no confusion can occur
between a preprocessing macro and a variable name.
Before using the template, one must instanciate it,
which is done with :

#define _QSORT_TYPE type(SORT_DATA)
#include "sorting_template.f90"

The "instanciation" is based, for example, on the following
SORT_DATA derived type, which may be defined in the
module "m_testsorting.f90" :

type SORT_DATA
integer key
integer index
end type SORT_DATA

Even if this derived type is quite simple, practical uses of this
method may include more complex data types.
The module "m_testsorting" has to be pre-processed before
being used. After pre-processing, the previous line has
been transformed to :

subroutine qsort ( array, compare )
type(SORT_DATA), dimension(:) :: array

With this method, generic source code templates can be
configured at will and allow to generate specific
sorting algorithms for whatever type of data.
This method can lead to more complex templates, with
no limit in the number of macros, that is, no limit
in the number of abstract data types in the template.

The main drawback is that the debugging process is not
possible interactively. This is because the source code
is generated at compile-time and the "template" does
not correspond to the post-processed one anymore.
One solution is to use manually the pre-processor to
generate the pre-processed template and to debug on that
later file.

Two other methods exist to manage to do generic programming
in fortran : the "include" statement and m4 macros.

The "include" fortran statement can be used so that the
target source code contains the definitions of an
abstract data type used in the template.
This method is used by Arjen Markus in the Flibs
library, for example in the linked list abstract
data structure :

http://flibs.sourceforge.net/linked_list.html

Here we suppose that the file "sorting_template.f90"
contains one sorting subroutine, which arguments
are defined like this :

subroutine qsort_array( array, compare )
type(SORT_DATA), dimension(:) :: array

The trick is to use the include fortran statement
to put that template source code into a context in
which the abstract data type is defined.
The following source code defines a module, the derived
type SORT_DATA and then include the template source code.

module m_testsorting
type SORT_DATA
integer key
integer index
end type SORT_DATA
contains
include "sorting_template.f90"
end module m_testsorting

The module m_testsorting is now providing the derived type
SORT_DATA and the methods to manage it, which are included
in the template. With a little more work, it is even possible
to make so that the final derived type has a name which
corresponds more to the one implemented by the abstraction,
as shown by Arjen Markus on the linkedlist example.

module MYDATA_MODULE
type MYDATA
character(len=20) :: string
end type MYDATA
end module
module MYDATA_LISTS
use MYDATA_MODULE, LIST_DATA => MYDATA
include "linkedlist.f90"
end module MYDATA_LISTS

The main advantage of the "include" method is that
it uses only fortran statements so that the debugging
process is possible interactively. Moreover, it is very
elegant.

Another method is to use m4 macros to generate source code
at compile-time. Gnu m4 is an implementation of the traditional
Unix macro processor. This method is used by Toby White the
in the Fox library :

http://uszla.me.uk/space/software/FoX/

The template source code is defined in files which have the .m4.
extension and the corresponding source code is defined in the
.F90 file. The source code may be difficult to maintain, but the
method is extremely powerful, thanks to the m4 features.
For example, one can use a m4 "for" loop to generate
several subroutine based on one template subroutine.
But, as for the pre-processing method, the interactive
debugging of the .m4 templates is not possible :
instead, the processed .f90 generated files are debugged easily
and directly.

Are there other well-known methods ?
Is there a definitive drawback for one of these methods so
that another way should be chosen?

All comments will be appreciated.

Best regards,

Michaël Baudin

Arjen Markus

unread,
Apr 15, 2008, 10:30:30 AM4/15/08
to
On 15 apr, 16:16, relaxmike <michael.bau...@gmail.com> wrote:
> Hi all fortran gurus !
>
> I would like to discuss the current methods to manage
> templates in fortran, following the thread
>
> http://groups.google.com/group/comp.lang.fortran/browse_thread/thread...

(One remark: part of the code you refer to was written
by Joe Krahn)

In Fortran 2003 you can use the class facilities and unlimited
polymorphic variables to solve these problems, but material
showing how to do that is limited so far.

Regards,

Arjen

Bil Kleb

unread,
Apr 15, 2008, 12:33:46 PM4/15/08
to
relaxmike wrote:
> Hi all fortran gurus !

Hi.

> Are there other well-known methods ?

Apparently not well-known, but there is:

http://www.macanics.net/forpedo/

Regards,
--
Bil Kleb
http://fun3d.larc.nasa.gov

Terence

unread,
Apr 15, 2008, 7:46:12 PM4/15/08
to
I wrote a commercial general sort/merge program (still in general
public use) that just reads in a text control file with :-
a) the input file name
b) the second input file name (for merges) or blank
c) the output file name
d) record length in bytes
e) then pretty much as many fields as you want, coded as
type,bytestart,bytelength,A/D [,and repeat]

You can also request a tag sort, where the file byte offeset addresses
of the sorted records are left in the output file (for cases where you
don't have twice the original data size available).

Of course it can't deal with arbitary fields beyond the expected
strings, integers, reals and T/F, nor does it sort variable length
records (but could easily changed to do so, nor does it cater for non-
MS byte order and FP formatting), but I see no need for the formal
route suggested.

I always prefer simple and practical.

Damian

unread,
Apr 16, 2008, 1:25:33 AM4/16/08
to
On Apr 15, 7:16 am, relaxmike <michael.bau...@gmail.com> wrote:
> Hi all fortran gurus !
>
> I would like to discuss the current methods to manage
> templates in fortran, following the thread
>
> http://groups.google.com/group/comp.lang.fortran/browse_thread/thread...

At least one text describes an approach equivalent to your pre-
processing approach: Object-Oriented Programming via Fortran 90/95 by
Ed Akin, Cambridge University Press, 2003. I don't have my copy at
hand but I'm pretty sure it's in Chapter 6.

Also, FWIW, some would argue that Fortran 2003 supports at least some
form of generic programming via parametrized derived types (definitely
not the same capability as templates but generic programming
nonetheless) and then there is the class(*) to which Arjen alludes.

Damian

Gerry Ford

unread,
Apr 16, 2008, 2:10:31 AM4/16/08
to

"Arjen Markus" <arjen....@wldelft.nl> wrote in message
news:0a01dacb-fcd7-4279...@b5g2000pri.googlegroups.com...

In Fortran 2003 you can use the class facilities and unlimited
polymorphic variables to solve these problems, but material
showing how to do that is limited so far.

--->Would you consider these class facilities and unlimited polymorphic
variables as "object-oriented?"

--
"Shopping for toilets isn't the most fascinating way to spend a Saturday
afternoon. But it beats watching cable news."

~~ Booman


Arjen Markus

unread,
Apr 16, 2008, 2:50:46 AM4/16/08
to
On 16 apr, 08:10, "Gerry Ford" <ge...@nowhere.ford> wrote:
> "Arjen Markus" <arjen.mar...@wldelft.nl> wrote in message

>
> news:0a01dacb-fcd7-4279...@b5g2000pri.googlegroups.com...
>
> In Fortran 2003 you can use the class facilities and unlimited
> polymorphic variables to solve these problems, but material
> showing how to do that is limited so far.
>
> --->Would you consider these class facilities and unlimited polymorphic
> variables as "object-oriented?"
>

They can certainly be used in that way - for a suitable
definition of "object-oriented". But the main point is
that they give you the possibility to define interfaces
that allow a wide range of actual types.

Regards,

Arjen

Arjen Markus

unread,
Apr 16, 2008, 2:57:17 AM4/16/08
to
> Damian- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

I must have a look again at Akin's book - the details
have escaped me ;).

Note that templates in C++ are completely different
beasts than what we are talking about here:

If you define a template A with one parameter T in C++ and
then define in various pieces of your code the same instance,
say a type A< int> in ten different source files,
the compiler will have to create ten different copies of
the actual object code that implements that particular
type.

It is up to the linker to make sure that in the final
executable program there is only one copy (or at least that
there is no conflict between them).

So, in C++ the task is much more complicated than just
generating specific source code from generic source code.


In Fortran we would have to generate the specific source
code for each specific type. And as Michael indicates
there are several methods for that. But in the end,
for the compiler and linker it is just a larger set
of ordinary code!

Regards,

Arjen

Arjen Markus

unread,
Apr 16, 2008, 2:59:23 AM4/16/08
to

I often use Tcl scripts or Fortran programs to
generate the specific code, if other methods
fail - fairly ad hoc, but it works splendidly.

Regards,

Arjen

relaxmike

unread,
Apr 16, 2008, 4:15:40 AM4/16/08
to
On 16 avr, 07:25, Damian <dam...@rouson.net> wrote:
> At least one text describes an approach equivalent to your pre-
> processing approach: Object-Oriented Programming via Fortran 90/95 by
> Ed Akin, Cambridge University Press, 2003. I don't have my copy at
> hand but I'm pretty sure it's in Chapter 6.

I have the book in my hands : "Object oriented programming
via fortran 90/95" by Ed Akin.
In fact, I was very enthousiast about the title, but the content
was very disappointing to me.
For me, the single article "Object-based programming in Fortran 90"
by Gray and Roberts is much more interesting, because it gives
the essence of OO :
http://www.ccs.lanl.gov/CCS/CCS-4/pdf/obf90.pdf

About the problem of generic code, here is the only part where the
subject is detailed, in chapter 3, p. 51 :
"None of the OOP languages have all the features one might desire.
For example, the useful concept of <<template>>, which is standard in C
++,
is not in the F90 standard. Yet the author has found that a few
dozen lines of F90 code will define a preprocessor that allows
templates
to be defined in F90 and expanded in line at compile time. [...]"

That is all : I did not find any paragraph showing how to practically
implement templates in fortran, a subject which could have been at
the core of the book with such a title.
The second edition of "Fortran 90/95 explained" does not deal
with that subject neither (but it may be done in the current edition,
I don't know).

Comparing to the methods I allready know, Forpedo seems very
interesting.
The tool is easy to implement within a Makefile, as for m4 macros.
The generic source code really looks like fortran source code,
even with the macros like @T and <T>.

I did the following table to compare the methods
- standard fortran : is the method in the fortran norm
- debug : is the template source code easy to debug
- features : does the method allow to manage complex templates
(this is only my point of view)

Standard Debug Features
Fortran
-----------------------------------------------------------
include * ** *
Pre-processing - * **
m4 - * ****
forpedo - * ***
fortran 2003 * ? ?

Michaël

Damian

unread,
Apr 16, 2008, 10:23:32 AM4/16/08
to

Keep looking. There is a short passage with more details later in the
book. Again, I'm pretty sure it's chapter 6, but I'll send an exact
page later when I have my copy with me. The reason I found it similar
to what you wrote is your use of the leading underscore, which you
explained was because Fortran variables names cannot start with an
underscore. In his case, he uses a trailing dollar sign ($) for a
similar reason. Look for the string "Template$". The reason I'm so
certain it's there is because I use his technique in my own code. I
learned it from his book and haven't seen it anywhere else.

Damian

Damian

unread,
Apr 16, 2008, 10:25:45 AM4/16/08
to

Excellent points! Thanks for the clarification.

Damian

relaxmike

unread,
Apr 16, 2008, 11:14:52 AM4/16/08
to
You are right, I did not remember that part, but I read
it.
This is in chapter 6, p144, "Templates".
My point of view is different from the one of the
author about that problem and I hope that this chapter
will be rewritten in the second edition, if ever.

Michaël

gunnar

unread,
Apr 16, 2008, 1:36:41 PM4/16/08
to
On 15 Apr, 16:16, relaxmike <michael.bau...@gmail.com> wrote:
> Hi all fortran gurus !
>
>
> 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.
>


Hi,

From what I have experienced, after starting to program in Fortran
after 15 years, is that there is very boring to write the same control
logic all over the program, especially when you have to make a minor
change and need to do the same update in many places. Not to metion
the probability to introduce errors by doing so. I also find it boring
to write the logic needed to be able to select a certain algorithm
during program execution.

As I see it, parametrized derived types and CLASS(*) has the intention
to let you do this, but I think that you have to include something
more or extend the these constructs to really have something that
helps.

One idea that I have: Would it not be possible to also parametrize the
procedures?

Something like this

subroutine bbbbb(E,F,H,D)
type,type :: A=(INTEGER,REAL),B=(TypeX,typeY),C
character(10), length :: D=("AlgorithmA")
type(A),allocateable :: E(:)
class(B) :: B
type(C) :: H
.
.
.
end


The compiler could then use the type of E and F, and the value of D,
to select the specific subroutine to use, and I could use D to select
the algorithm to use during program execution.


Regards Gunnar


GaryScott

unread,
Apr 16, 2008, 4:39:46 PM4/16/08
to
On Apr 16, 12:36 pm, gunnar <bjorkm...@gmail.com> wrote:
> On 15 Apr, 16:16, relaxmike <michael.bau...@gmail.com> wrote:
>
<snip>

> From what I have experienced, after starting to program in Fortran
> after 15 years, is that there is very boring to write the same control
> logic all over the program, especially when you have to make a minor
> change and need to do the same update in many places. Not to metion
> the probability to introduce errors by doing so. I also find it boring
> to write the logic needed to be able to select a certain algorithm
> during program execution.

While I have experienced this code duplication "problem" on rare
occasion, I find that I fail to see that it is a great programming
concern. I don't oppose adding such capabilities to the language, but
please make it a formal part of the language and not some add-on that
will be poorly supported like a pre-processor that largely, probably,
poorly duplicates functionality of a pre-existing native JCL or macro
language or a more popular preprocessor for another language.

<snip>

Regards Gunnar

relaxmike

unread,
Apr 17, 2008, 7:58:16 AM4/17/08
to
On 16 avr, 22:39, GaryScott <garylsc...@sbcglobal.net> wrote:
> While I have experienced this code duplication "problem" on rare
> occasion, I find that I fail to see that it is a great programming
> concern.

That really depends on what kind of problems you have to solve.
"If you only have a hammer, everything you see is a nail."

Suppose you have to connect a fortran software
with a Java platform and transfer data between the two with
sockets. The subroutines which are used to manage the send/receive
are complex and have to be able to manage all basic
fortran data types : integer, real, character, and all possible
arrays from rank 1 to rank 7. Let us count 3 * 7 = 21 subroutines
to send data and 21 subroutines to receive data. But you also
have to manage errors (21 more), etc...
In short : the connection is not possible to do in fortran without
fortran templates. The conclusion is : "Aaahh, that fortran language
is crap, let's do it in C++ and the job will be done.".
You think that this case cannot happen ?
Yes it can happen, and it really happened 4 years ago in my last job.
So my colleagues used pre-processing, which is a very good idea,
and that worked.

In the case of Fox, that tool would simply not exist if fortran
templates did not exist (based on m4 in that case).

The fact that "fortran is crap" or that "fortran templates are not
a great concern" depends on what solutions you can put in front of
the problems you have.

Regards,

Michaël Baudin

Gary Scott

unread,
Apr 17, 2008, 8:27:21 AM4/17/08
to
relaxmike wrote:
> On 16 avr, 22:39, GaryScott <garylsc...@sbcglobal.net> wrote:
>
>>While I have experienced this code duplication "problem" on rare
>>occasion, I find that I fail to see that it is a great programming
>>concern.
>
>
> That really depends on what kind of problems you have to solve.
> "If you only have a hammer, everything you see is a nail."
>
> Suppose you have to connect a fortran software
> with a Java platform and transfer data between the two with
> sockets. The subroutines which are used to manage the send/receive
> are complex and have to be able to manage all basic
> fortran data types : integer, real, character, and all possible
> arrays from rank 1 to rank 7. Let us count 3 * 7 = 21 subroutines
> to send data and 21 subroutines to receive data. But you also
> have to manage errors (21 more), etc...
> In short : the connection is not possible to do in fortran without
> fortran templates. The conclusion is : "Aaahh, that fortran language
> is crap, let's do it in C++ and the job will be done.".

I've been doing sockets without templates for 15 years. I'm not
familiar enough with your above application comment on it though. I
didn't imply there was no use for templates. I was suggesting that if
we implement them, do it right, not through some half-a$$ preprocessor
method. I oppose preprocessors, because whatever gets put in the
standard will basically be inferior to tools I had on one or another
operating system in the past and I'll always know how inferior it is as
I try to force it to do things it wasn't designed for.

<snip>
>
> Regards,
>
> Michaël Baudin


--

Gary Scott
mailto:garylscott@sbcglobal dot net

Fortran Library: http://www.fortranlib.com

Support the Original G95 Project: http://www.g95.org
-OR-
Support the GNU GFortran Project: http://gcc.gnu.org/fortran/index.html

If you want to do the impossible, don't hire an expert because he knows
it can't be done.

-- Henry Ford

relaxmike

unread,
Apr 17, 2008, 11:18:58 AM4/17/08
to
On 17 avr, 14:27, Gary Scott <garylsc...@sbcglobal.net> wrote:
> I oppose preprocessors, because whatever gets put in the
> standard will basically be inferior to tools I had on one or another
> operating system in the past and I'll always know how inferior it is as
> I try to force it to do things it wasn't designed for.

You made a point here, because the pre-processing method is
difficult to debug directly, except if you debug the pre-processed
source code instead of the template one.
This can be easily solved though, via makefile commands such as :

%.f90 : %.F90
$(FPP) $(FPPOPTS) $< -P $@

%.o : %.f90
$(FC) $(F90OPTS) -c $< -o $@

That is to say that the .F90 contains the template to pre-process.
make start to compute the .f90 specific source code, depending
on the template one.
It is only the .f90 pre-processed source code which has to be
compiled. This allows to debug interactively the specific
source code, while maintaining only the template one.
The "-P" option of the fortran preprocessor may allow to remove
the lines introduced by the preprocessor in the preprocessed
file which points to the line number in the original template.

Now that I have revealed all my very secret tricks (!!!), I don't
see any main drawback to using pre-processing as a template method,
except that the features implemented in many pre-processor are very
poor, compared to what is possible in Python, m4 or Tcl for example.

Most compilers include a pre-processor inside the
compiler itself, for example Intel Fortran, gfortran, g95, Sun,
Ibm, etc...
Even if the pre-processor is not included in
your favorite compiler, or if you dislike the features
that it provides, you can use an external one, for example
the C pre-processor. If the C pre-processor is not available
on the machine (which has a very low probability, but is possible),
you can get the source of gcc and compile your own release.

In fact, the main reason why pre-processing macros are not used
widely in fortran is for historical reasons, I suppose, and by the
fact
that many fortran developers are developing only in fortran,
with high-level skills in science, most of the time, and, most
of the time, low-level skills in software : there are many,
many exceptions to this general fact, and these exceptions are
the core of this forum.
Put a C developer in front of a fortran source, and he will
introduce a new pre-processing macro every 3 minutes...

What I am curious is at the fortran 2003 features which
allows to do this. I think I'm going to read the standard
again.

Best regards,

Michaël

Richard Maine

unread,
Apr 17, 2008, 12:29:54 PM4/17/08
to
relaxmike <michael...@gmail.com> wrote:

> Suppose you have to connect a fortran software
> with a Java platform and transfer data between the two with
> sockets. The subroutines which are used to manage the send/receive
> are complex and have to be able to manage all basic
> fortran data types : integer, real, character, and all possible
> arrays from rank 1 to rank 7. Let us count 3 * 7 = 21 subroutines
> to send data and 21 subroutines to receive data. But you also
> have to manage errors (21 more), etc...
> In short : the connection is not possible to do in fortran without
> fortran templates.

I agree that something like templates can be useful in some situations.
But note that, depending on details, the particular scenario described
above can usually be handled better in other ways. Typically, all you
need is an equivalent of a C void pointer, along with the data size.
Then you can do it all in a single procedure, with no need for
templates. I've done this exact thing for a long time, as I also have
Fortran code that passes data via sockets. So have other people. This
one is done a lot.

You end up with a single procedure, rather than the multiplicity of
procedures that the template approach involves. The template approach
makes it more practical to create that multiplicity of procedures, but
they still do end up having to get created.

In Fortran 90/95, you have to use tricks that aren't technically
standard conforming, but still have decent portability in practice, in
order to do the needed "type cheating". In Fortran 2003, you can use
either the C interop features or the unlimitted polymorphic feature to
make such code standard conforming.

--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain

relaxmike

unread,
Apr 18, 2008, 6:07:30 AM4/18/08
to
On 17 avr, 18:29, nos...@see.signature (Richard Maine) wrote:
> I agree that something like templates can be useful in some situations.
> But note that, depending on details, the particular scenario described
> above can usually be handled better in other ways. Typically, all you
> need is an equivalent of a C void pointer, along with the data size.
> Then you can do it all in a single procedure, with no need for
> templates. I've done this exact thing for a long time, as I also have
> Fortran code that passes data via sockets. So have other people. This
> one is done a lot.
>
> You end up with a single procedure, rather than the multiplicity of
> procedures that the template approach involves. The template approach
> makes it more practical to create that multiplicity of procedures, but
> they still do end up having to get created.

I don't understand how to implement this way. What kind of fortran
data type corresponds to a C void pointer ?
Let's take a practical example or reading a data in a string.
The following source code read a data in an internal file
and returns the value read, depending on the type of variable.
It is over-simplified to make the point shorter.
It does not use fortran templates, but only standard fortran.

interface readfile_data
module procedure readfile_data_logical
module procedure readfile_data_integer
end interface readfile_data

subroutine readfile_data_logical ( current_data_line , myvalue ,
status )
implicit none
character ( len = 200 ) , intent(in) :: current_data_line
logical , 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_logical

subroutine readfile_data_integer ( current_data_line , myvalue ,
status )
implicit none
character ( len = 200 ) , intent(in) :: current_data_line
integer , 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_integer

It is very important to factorise this small code, because the
interface
could be extended way beyond the current state, for example reading
more
complex data types :

interface readfile_data
module procedure readfile_data_logical
module procedure readfile_data_integer
module procedure readfile_data_real
module procedure readfile_data_double
module procedure readfile_data_logical_array1
module procedure readfile_data_integer_array1
module procedure readfile_data_real_array1
module procedure readfile_data_double_array1
module procedure readfile_data_logical_array2
module procedure readfile_data_integer_array2
module procedure readfile_data_real_array2
module procedure readfile_data_double_array2
[etc... until the maximum rank size available in fortran is reached
= 7]
end interface readfile_data

with the following header for the readfile_data_double_array2
subroutine :

subroutine readfile_data_double_array2 ( current_data_line ,
myvalue , status )
implicit none
character ( len = 200 ) , intent(in) :: current_data_line
real(dp), dimension(:,:) , intent (out) :: myvalue
integer, intent(out) :: status

Developing the code with standard fortran is near to impossible,
although hiring a PhD with European funds to do this is allways
possible...
With fortran templates, it is really easy.
Suppose that the file "readfile_data_template.F90" contains the
following
source code, parametrized with the "_DATATYPE" macro.

subroutine readfile_data__DATATYPE ( current_data_line , myvalue ,
status )
implicit none
character ( len = 200 ) , 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_integer

The previous source code would be using the template like this :

interface readfile_data
module procedure readfile_data_logical
module procedure readfile_data_integer
end interface readfile_data

#define _DATATYPE logical
#include "readfile_data_template.F90"

#define _DATATYPE integer
#include "readfile_data_template.F90"

And now, with only 6 lines of source code, extending it to
manage for several data types, and arrays of all possible
sizes is possible. The same problem would be even easier to
manage with Forpedo, I suppose.

Now, how do you suggest to solve the problem with your method
Richard ? What is the fortran type which corresponds to the
C void pointer ?

Regards,

Michaël

relaxmike

unread,
Apr 18, 2008, 8:51:03 AM4/18/08
to
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

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


Of course, dealing with that implementation consumes a lot of memory,
especially if we include arrays of integers, arrays of logicals,
etc...
To solve that, we can use fortran 90 pointers and allocate only the
type under use. Since the abstract data type is more complex, it
is now time for full OO.

module m_moduletest5
type :: DATATYPE
integer, pointer :: value_integer => NULL()
logical, pointer :: value_logical => NULL()
end type DATATYPE
contains
subroutine data_newfromstring ( this , current_data_line ,
classname , status )
implicit none
character ( len = * ) , intent(in) :: current_data_line
character ( len = * ) , intent(in) :: classname
type ( DATATYPE ) , intent(out) :: this


integer, intent(out) :: status
status = 0

select case ( classname )
case ( "integer" )
allocate ( this % value_integer )
read ( current_data_line , * , err = 100 , end = 100) this %
value_integer
case ( "logical" )
allocate ( this % value_logical )
read ( current_data_line , * , err = 100 , end = 100) this %
value_logical
case default
write(6,*) "Bad classname."
end select


return
100 continue
status = 1

end subroutine data_newfromstring
subroutine data_print ( this )
implicit none
type ( DATATYPE ) , intent(in) :: this
if ( associated ( this % value_integer ) ) then
write ( *, * ) "integer :"
write ( * , * ) this % value_integer
elseif ( associated ( this % value_logical ) ) then
write ( *, * ) "logical :"
write ( * , * ) this % value_logical
endif
end subroutine data_print
subroutine data_free ( this )
implicit none
type ( DATATYPE ) , intent(in) :: this
if ( associated ( this % value_integer ) ) then
deallocate ( this % value_integer )
elseif ( associated ( this % value_logical ) ) then
deallocate ( this % value_logical )
endif
end subroutine data_free

end module m_moduletest5

program test5
use m_moduletest5
character (len=200) :: current_data_line
type ( DATATYPE ) :: myvalue
integer :: status
current_data_line = "1"
call data_newfromstring ( myvalue , current_data_line , "integer",
status )
write ( * , * ) "status:", status
call data_print ( myvalue )
call data_free ( myvalue )
current_data_line = ".true."
call data_newfromstring ( myvalue , current_data_line , "logical" ,
status )
write ( * , * ) "status:", status
call data_print ( myvalue )
call data_free ( myvalue )
end program test5

I admit that the current code is still manageable, but have many
limitations
that the pre-processing version have not, including :
- the memory is managed at hand, which can lead to memory leaks.
This is easy to manage with only 2 basic types, but what if there are
21 ?
- 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.
On the good side, the source code is very easy to debug and uses only
standard fortran 90 statements.

Still the current "pointer to everything" is a heavy solution.
What if we wand to define a "writetostring" method : another 21*3
block of source code. And what if we want to define an error
system for the class : another 21*3 block of source code !
This is not practical.

But you may suggest another way ?

Best regards,

Michaël

Richard Maine

unread,
Apr 18, 2008, 11:13:22 AM4/18/08
to
relaxmike <michael...@gmail.com> wrote:

> What kind of fortran
> data type corresponds to a C void pointer ?

Type(C_PTR) in the F2003 C interop stuff.

James Van Buskirk

unread,
Apr 18, 2008, 2:31:00 PM4/18/08
to
"relaxmike" <michael...@gmail.com> wrote in message
news:5be1dfe2-56d4-4a32...@k37g2000hsf.googlegroups.com...

> end module m_moduletest2

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

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


Charles Coldwell

unread,
Apr 20, 2008, 3:10:02 PM4/20/08
to
relaxmike <michael...@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

relaxmike

unread,
Apr 21, 2008, 8:27:30 AM4/21/08
to
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

Richard Maine

unread,
Apr 21, 2008, 10:52:18 AM4/21/08
to
relaxmike <michael...@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.

James Van Buskirk

unread,
Apr 21, 2008, 12:43:05 PM4/21/08
to
"relaxmike" <michael...@gmail.com> wrote in message
news:ffd371af-6da7-44b4...@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

James Van Buskirk

unread,
Apr 21, 2008, 1:00:38 PM4/21/08
to
"Richard Maine" <nos...@see.signature> wrote in message
news:1ifqlj2.1nemrlg1h3158cN%nos...@see.signature...

> relaxmike <michael...@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.

The problem of ordering should not arise in practice because the
normal thing to do given what is forced upon the user by F90 rules is
to declare the user-defined type in one module along with assignment(=)
and any operators or procedures you want to overload for the type.
Then this module is USEd in the module that defines the template
procedures, which after all need assignment(=) and operators and
procedures overloaded for the type in the body of any template
procedures.

> 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.

This objection shows that you have never done template programming
in C++. It turns out to be so gawdawful that the normal way to
develop template code is to start with a non-template class and
methods, get that working OK, then convert to templates. Working
in an analogous fashion in Fortran, the non-template version would
have IMPLICIT NONE in force and only when it's working OK would
implicit typing be introduced. Besides IMPLICIT NONE won't help
with host association until the committee gives us some way to
restrict host association.

The really ugly part is: what are you supposed to do when you
need more than 26 template types? Maybe a Chinese Fortran
compiler would become popular at this point. Code with > 26
template types would be harder to read than Chinese, for sure.

glen herrmannsfeldt

unread,
Apr 21, 2008, 4:01:41 PM4/21/08