Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

any reason to avoid using preprocessor directives?

524 views
Skip to first unread message

benjamin....@noaa.gov

unread,
Feb 28, 2018, 3:13:00 PM2/28/18
to
A colleague is rewriting some legacy code, and has added preprocessor directives to the code (primarily #ifdef type constructs). I could write the code without requiring the preprocessor directives. Are there are any specific objections to using preprocessor directives when they can be avoided (with extra effort)?

I'm mostly concerned about portability (between ifort, gfortran, and ftn), but I am ignorant and my concern is based on a lack of knowledge.

Anyway, nothing serious, just curious if I'm missing something on this topic.



FortranFan

unread,
Feb 28, 2018, 3:50:12 PM2/28/18
to
On Wednesday, February 28, 2018 at 3:13:00 PM UTC-5, benjamin....@noaa.gov wrote:

> A colleague is rewriting some legacy code, and has added preprocessor directives to the code (primarily #ifdef type constructs). I could write the code without requiring the preprocessor directives. Are there are any specific objections to using preprocessor directives when they can be avoided (with extra effort)?
> ..


As a conscientious US taxpayer and someone who personally only ever resorts to preprocessor directives when needing to work with certain system-level or OS-specific APIs, I fervently hope any Fortran code toward NOAA or any national/federal agency initiatives will be primarily based on the IEC ISO standard.

I personally think it is more effort to write and work with code that uses preprocessor directives, that additional bugs can be introduced with their use than otherwise. This can lead to added validation and maintenance costs, not to mention possible barriers to portability.

Consider an example of a Fortran interface to Microsoft Windows API, GetModuleFileName (https://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx) on x86 and x64 target platforms. An option will be as follows

--- begin code snippet ---
..
use, intrinsic :: iso_c_binding, only : c_char, c_int, c_intptr_t
..
!.. Mnemonics for types in Windows API functions
integer(c_int), parameter :: HMODULE = c_intptr_t !.. A handle to a module; base address in memory
integer(HMODULE), parameter :: NULL_HANDLE = int( 0, kind=kind(NULL_HANDLE) )
integer(c_int), parameter :: DWORD = c_int
integer(c_int), parameter :: MAX_PATH = 260

interface

function GetModuleFileName(ModuleName, lpFilename, nSize) bind(C, name='GetModuleFileNameA') result(RetVal)

import :: HMODULE, DWORD, c_char

implicit none

!.. Argument list
integer(kind=HMODULE), value, intent(in) :: ModuleName
character(kind=c_char,len=1), intent(inout) :: lpFilename(*)
integer(kind=DWORD), value, intent(in) :: nSize
!.. Function result
integer(kind=DWORD) :: RetVal

#ifdef _WIN32
#ifdef __GFORTRAN__
!GCC$ ATTRIBUTES STDCALL :: GetModuleFileName
#elif __INTEL_COMPILER
!DIR$ ATTRIBUTES STDCALL :: GetModuleFileName
#endif
#endif

end function GetModuleFileName

end interface
--- begin code snippet ---

Now in the above instance, the Fortran standard cares nothing about STDCALL convention followed by Microsoft on x86 systems, so there isn't any choice other than the directives. But you can see even with such a simple API, the various tokens and if-else- logic that can come into play and the complexity that is introduced in the code as a result. And yet after all of above effort, chances are the code will *not* work with any toolset besides Intel Fortran and gfortran.

Now imagine resorting to such #ifdef or !DIR$/!GCC$ shenanigans for any compute instructions that are already standardized in Fortran as per IEC ISO 1539-1. That will be tremendous disservice indeed to taxpayers. I hope you manage to convince your colleague to cease and desist from any such employ of preprocessor directives.

Thanks for reaching out on this forum,

David Duffy

unread,
Feb 28, 2018, 6:46:09 PM2/28/18
to
benjamin....@noaa.gov wrote:
> A colleague is rewriting some legacy code, and has added preprocessor
> directives to the code (primarily #ifdef type constructs). I could
> write the code without requiring the preprocessor directives. Are there
> are any specific objections to using preprocessor directives when they
> can be avoided (with extra effort)?
>
Unlike FF, I'm not too concerned with added complexity - and you will
never be in an environment where fpp/cpp will be unavailable. I reckon
it's an aesthetic thing, and how much work it adds for you. Obviously,
there must be gains sometimes from macro inlining in the way C (and Lisp)
programmers reflexively do, but I wouldn't usually chase these, as the
compilers these days should be smart enough.

James Van Buskirk

unread,
Feb 28, 2018, 9:47:40 PM2/28/18
to
"FortranFan" wrote in message
news:bd5f1f90-7131-43f1...@googlegroups.com...

> #ifdef _WIN32
> #ifdef __GFORTRAN__
> !GCC$ ATTRIBUTES STDCALL :: GetModuleFileName
> #elif __INTEL_COMPILER
> !DIR$ ATTRIBUTES STDCALL :: GetModuleFileName
> #endif
> #endif

> Now in the above instance, the Fortran standard cares nothing
> about STDCALL convention followed by Microsoft on x86 systems,
> so there isn't any choice other than the directives. But you can
> see even with such a simple API, the various tokens and if-else-
> logic that can come into play and the complexity that is
> introduced in the code as a result. And yet after all of above effort,
> chances are the code will *not* work with any toolset besides Intel
> Fortran and gfortran.

But you don't need all those hash tags! If you got rid of the 5
preprocessor lines the code would still work on both ifort and
gfortran, 32- or 64-bit code.

Preprocessor directives are insidious menace and break otherwise
valid Fortran code. If you have a preprocessor directive in a
template file then all the Fortran INCLUDE statements that refer
to that file have to be changed to #include directives, out to the
highest level of inclusion.

Fortran's KINDs, modules, and initialization expressions obviate
many of the contexts where preprocessor directives would
otherwise be necessary. Using preprocessor directives is another
way that C sort of creeps into and breaks the Fortran language,
like with C_BOOL, which even some C compilers don't support
yet still has irreparably broken Fortran LOGICAL variables.

Stefano Zaghi

unread,
Mar 1, 2018, 12:03:07 AM3/1/18
to
Dear FortranFan,

In general I agree, I like to retain and to stick in pure fortran ad much as possible. However, there at least few cases where I was not able to find an effective pure fortran approach.

One of those cases is "quick and dirty" debug: often, I exploit print statement to debug procedure but this is not possible if the procedure is pure. I know that with oop I can design an error object to handle errors state, but this is not as quick as print the state. In this case, I exploit c preprocessor that is quite widespread and robust, something on the following line

#ifdef DEBUG
#define XPURE
#else
#define XPURE PURE
#enfif

...

XPURE subroutine foo(...

At the end this approach adds more or less 5 lines, but I can easily switch between pure and impure (print-like debug enabled) modes. What do you think about?

Cheers

spectrum

unread,
Mar 1, 2018, 1:56:54 AM3/1/18
to
I use this kind of thing to show file names + some string...

#define _stop_(x) stop __FILE__ // " : " // x

I think any preprocessors (by using any tools like cpp, Python, OCaml, etc)
are often used to provide some functionalities that are
currently not available in pure Fortran (or, doable but become
very tedious to achieve...), e.g. "poor man's template" approaches.
So in my case I feel it depends on cases (at the moment).

spectrum

unread,
Mar 1, 2018, 1:28:05 PM3/1/18
to
On the other hand, some code uses C preprocessors almost in a spaghetti way
to add more and more features (using a lot of entangled #ifdef and #endif).
In the worst case, the code becomes almost unreadable... I experienced it
before for some molecular-dynamics code, for which people keep on
adding new features in a non-systematic way, resulting in a very unreadable
code (they call that program "a vehicle for new experimental features" :-)

I think one case that needs C preprocessors is probably "#ifdef MPI" to add
parallel codes to a serial one (though it may become not necessary when Coarray
becomes more easily available anywhere).

Ron Shepard

unread,
Mar 1, 2018, 8:58:56 PM3/1/18
to
Because the C preprocessor was designed for the C language, there are
some quirks when using it for fortran source code. I don't remember the
details now, but I once had a sequence of characters in a fortran code
that just happened to correspond to a C tri-graph. It took a while for
me to understand what was happening, but there was an easy way to
rewrite the code that avoided the problem.

Of course, you can write indecipherable macros and conditional
compilation in the C preprocessor, so you don't want to do that. And the
C preprocessor will substitute anything that looks like a defined macro,
so you sometimes need to be careful in how you define macros so that
they do not conflict with variables names, subprogram names, and so on.

At one time, the C preprocessor was almost necessary to write portable
code. Not only conditional compilation for OS-dependent or
hardware-dependent features, but also such things as #include could only
be done that way. Modern fortran however is more flexible and does many
of these things now.

A few things that the preprocessor is still useful for is conditional
compilation (still necessary, even today) and embedding time, date, and
filename strings into the compiled code. It also makes it easy to embed
macros defined on the compilation command line into the compiled code.

Most fortran compilers allow easy access to the C preprocessor, either
with a command line option or sometimes just through source code file
names (*.f vs. *.F or *.f90 vs. *.F90). If some compiler does not have
that built in, then you can also access standalone preprocessors
independently. One that I have used in the past is filepp.

http://www-users.york.ac.uk/~dm26/filepp/

$.02 -Ron Shepard

urba...@comcast.net

unread,
Mar 3, 2018, 12:48:17 AM3/3/18
to
I strongly advocate minimal use of conventional use of preprocessors to provide
conditional compilation for some of the same reasons stated above -- I too have inherited code using so many cpp directives that the code is virtually unreadable, for example.

And in my experience conditional compilation is needed far less frequently than in the past. On the other hand, almost every routine I have created recently uses pre-processing directives -- but almost exclusively to add functionality Fortran does not provide. So from my relatively unique vantage point ( I wrote a non-macro pre-processor) I find pre-processors invaluable in extending Fortran files to allow for block text, keeping documentation and multi-language code sections (Fortran and C, for example) and unit tests (including data files) in the same file, and so. So in addition to the previous responses I'd just like to mention the answer can depend very much on what pre-processor you use, and whether you are using it for simple conditional compilation or for other reasons. I wrote my general opinion on this issue long ago towards the bottom of the document at
http://www.urbanjost.altervista.org/LIBRARY/libGPF/download/tmp/html/ufpp_overview.7.html

if you are interested.

FortranFan

unread,
Mar 3, 2018, 11:24:11 AM3/3/18
to
On Thursday, March 1, 2018 at 12:03:07 AM UTC-5, Stefano Zaghi wrote:

> .. there at least few cases where I was not able to find an effective pure fortran approach.
>
> One of those cases is "quick and dirty" debug: .. What do you think about?
> ..


Hi Stefano,

As we have discussed previously, graphical debuggers in IDEs e.g., Visual Studio can help you do away with the need to rely on print statements for debugging. Your PURE subprograms can remain 'pure' then!

Anyways with cases such as those with OP where two (or more) developers are involved and the code is expectedly part of some institutional effort, debugging needs is hopefully integral to code design and coders avoid "quick and dirty" print statements, for they have a tendency to persist and multiply and 'uglify' code!

FortranFan

unread,
Mar 3, 2018, 11:46:36 AM3/3/18
to
On Thursday, March 1, 2018 at 1:56:54 AM UTC-5, spectrum wrote:

> ..
> I think any preprocessors (by using any tools like cpp, Python, OCaml, etc)
> are often used to provide some functionalities that are
> currently not available in pure Fortran (or, doable but become
> very tedious to achieve...), e.g. "poor man's template" approaches.
> So in my case I feel it depends on cases (at the moment).


With "it depends on cases" in mind, the particular institutional effort alluded by the original post seems like that case where the preprocessing directives are best avoided.

Bálint Aradi

unread,
Mar 4, 2018, 7:57:34 AM3/4/18
to
I think, you should avoid preprocessor directives in general, if you can write your code easily without them. Sometimes, however, it is not that easy to avoid them. Several posters mentioned conditional compilation.

Another possible use case are templates (meta-programming), where with the help of preprocessor macros you could prevent code duplication. Especially in case of generic libraries, where you often wish to offer the same functionality for different data types (real, complex) or precisions (single and double precision) and the implementations for all those cases are often identical apart of the data type declaration. You would need, however, a Fortran-preprocessor offering loops, like for example Fypp (disclamer: I am the author of Fypp). Then your library module may look as follows:

module mylibrary
implicit none

#:set TYPES_AND_NAMES = [('real(sp)', 'real'), ('real(dp)', 'dreal')]

interface myroutine
#:for _, NAME in TYPES_AND_NAMES
module procedure myroutine_${NAME}$
#:endfor
end interface myroutine

contains

#:for TYPE, NAME in TYPES_AND_NAMES

subroutine myroutine_${NAME}$(arg)
${TYPE}$, intent(inout) :: arg
...
end subroutine myroutine_${NAME}$

#:endfor

end module mylibrary

Of course, it is a matter of taste, whether you prefer evil macros or evil code duplication more. ;-) I usually try to find a decent balance between them.

Best, Bálint

FortranFan

unread,
Mar 4, 2018, 2:39:36 PM3/4/18
to
On Sunday, March 4, 2018 at 7:57:34 AM UTC-5, Bálint Aradi wrote:

> ..
> Another possible use case are templates (meta-programming)..
>
> Of course, it is a matter of taste, whether you prefer evil macros or evil code duplication more ..


A couple of aspects readers can keep in mind:

1) If "templates (meta-programming)" facilities are so damn important to an initiative in a manner Fortran proves deficient, then consider something else besides Fortran.

2) But then if the circumstances conspire in a way where one has to still work with Fortran, then conform to IEC ISO 1539-1. For the use case of providing a generic interface for different (floating-point) types, why does it have to be binary choice of "evil macros or evil code duplication"?

IEC ISO 1539-1 provides INCLUDE which is in no way infinite in its wickedness compared to macros or code duplication. If anything, INCLUDE, though old-fashioned, is less "evil" compared to macros or code duplication: it's standard and it makes the code more readable, less error-prone, and maintainable.

module mylibrary

implicit none
..

interface myroutine
module procedure myroutine_real
module procedure myroutine_dreal
end interface myroutine

contains

subroutine myroutine_real(arg)
real(sp), intent(inout) :: arg
include 'xxx' ! implementation
end subroutine myroutine_real

subroutine myroutine_dreal(arg)
real(dp), intent(inout) :: arg
include 'xxx' ! implementation
end subroutine myroutine_dreal

end module mylibrary

So the point is the use cases mentioned thus far do not appear to me to be compelling enough for any reliance on preprocessing in an institutional coding effort. What I see are a bunch of MeatLoafisms: I'll do anything for ..., but I won't do that!! I just don't want that on my dime.

Ev. Drikos

unread,
Mar 4, 2018, 4:53:35 PM3/4/18
to
On 04/03/2018 21:39, FortranFan wrote:
> ...
> 2) But then if the circumstances conspire in a way where one has to still work with Fortran, then conform to IEC ISO 1539-1. For the use case of providing a generic interface for different (floating-point) types, why does it have to be binary choice of "evil macros or evil code duplication"?
>
> IEC ISO 1539-1 provides INCLUDE which is in no way infinite in its wickedness compared to macros or code duplication. If anything, INCLUDE, though old-fashioned, is less "evil" compared to macros or code duplication: it's standard and it makes the code more readable, less error-prone, and maintainable.
>
> module mylibrary
>
> implicit none
> ..
>
> interface myroutine
> module procedure myroutine_real
> module procedure myroutine_dreal
> end interface myroutine
>
> contains
>
> subroutine myroutine_real(arg)
> real(sp), intent(inout) :: arg
> include 'xxx' ! implementation
> end subroutine myroutine_real
>
> subroutine myroutine_dreal(arg)
> real(dp), intent(inout) :: arg
> include 'xxx' ! implementation
> end subroutine myroutine_dreal
>
> end module mylibrary
>
> So the point is the use cases mentioned thus far do not appear to me to be compelling enough for any reliance on preprocessing in an institutional coding effort...

@FortranFan

Can possibly Sub-Modules offer some flexibility (even restricted) in
such cases?

Your advice will be highly appreciated.

Thank you
Ev. Drikos

FortranFan

unread,
Mar 4, 2018, 10:43:30 PM3/4/18
to
On Sunday, March 4, 2018 at 4:53:35 PM UTC-5, Ev. Drikos wrote:

> ..
> Can possibly Sub-Modules offer some flexibility (even restricted) in
> such cases?
> ..

@Ev. Drikos,

SUBMODULEs in Fortran, as far as I understand them, can promote interface-driven design (IDD) by allowing the separation of interfaces from implementation and in the course of doing so, it can help prevent compilation cascades too. The former is a significant asset in modern code development and the latter a particular boon to large applications. SUBMODULEs are thus a great facility introduced starting with Fortran 2008.

But I think this feature, in and of by itself, does not advance the cause of generic (meta) programming in Fortran. For the case at hand involving a generic interface to a subprogram making the same operations with dummy arguments of different types, the implementation can be in SUBMODULEs but I fail to see how SUBMODULEs can help prevent code duplication. INCLUDE is, in my opinion, what the standard offers presently in this regard. Hopefully Fortran 202X will introduce further support in this area.

Cheers,

Bálint Aradi

unread,
Mar 4, 2018, 11:30:07 PM3/4/18
to
>
> IEC ISO 1539-1 provides INCLUDE which is in no way infinite in its wickedness compared to macros or code duplication. If anything, INCLUDE, though old-fashioned, is less "evil" compared to macros or code duplication: it's standard and it makes the code more readable, less error-prone, and maintainable.

Sure, include is a nice standard conforming solution as long as you have to implement one single generic function only. But if you have a lots of them as in a library, I'd prefer simple preprocessor-loops as shown above over having to have a separate include file for every generic function, especially if some of them are only few-liners. But neither solution is optimal, and it is matter of taste, which one you dislike less.

And of course, you can always change to a language with proper support for templates. But often enough, the simplicity Fortran offers for implementing all the number crunching within the routines may outweigh the headache you get by using a preprocessors.

Best,

Bálint

Ev. Drikos

unread,
Mar 5, 2018, 10:20:52 AM3/5/18
to
On 05/03/2018 05:43, FortranFan wrote:
> On Sunday, March 4, 2018 at 4:53:35 PM UTC-5, Ev. Drikos wrote:
>
>> ..
>> Can possibly Sub-Modules offer some flexibility (even restricted) in
>> such cases?
>> ..
> But I think this feature, in and of by itself, does not advance the cause of generic (meta) programming in Fortran. For the case at hand involving a generic interface to a subprogram making the same operations with dummy arguments of different types, the implementation can be in SUBMODULEs but I fail to see how SUBMODULEs can help prevent code duplication. INCLUDE is, in my opinion, what the standard offers presently in this regard. Hopefully Fortran 202X will introduce further support in this area.
>

@FortranFan

At first I acknowledge that without templates support the particular
example you have posted needs an implementation included twice, or one
would have to duplicate code. It's clearly understood.

I posted my question having in mind quite simpler cases, like the one
coded at the end of this message (see the pivot point 'x').

I wrote the Module in a manner that takes advantage of the preprocessor
without pre-processing directives. The Module contains the API. The
SubModule contains the implementation.

It doesn't need to be a trick though. One can manually duplicate the
Module in different files, one per type, but all Modules can share a
common SubModule.

Maybe I'm doing sth wrong here as I have the impression that the Fortran
compiler I use (gfortran 4.8 - 7.3) doesn't support parameterized types.
In any case, I don't feel comfortable enough with my Fortran skills. Is
there a better way to reuse code with this example?


Thank you,
Ev. Drikos

PS: it's an open question and replies from other posters are also welcome.

---------------

$ cat qsort_t.F90

module qsort_t

private
public :: qsort

interface

recursive module subroutine qsort(A)
real, intent(in out), dimension(:) :: A
end subroutine qsort

module subroutine Partition(A, marker)
real, intent(in out), dimension(:) :: A
integer, intent(out) :: marker
end subroutine

end interface

real :: temp
real :: x ! pivot point

end module qsort_t

$ cat qsort_s.F90
submodule (qsort_t) qsort_s
contains

module procedure qsort
integer :: iq
if(size(A) > 1) then
call partition(A, iq)
call qsort(A(:iq-1))
call qsort(A(iq:))
endif
end procedure qsort

module procedure partition
integer :: i, j
x = A(1)
i= 0
j= size(A) + 1
do
j = j-1
do
if (A(j) <= x) exit
j = j-1
end do
i = i+1
do
if (A(i) >= x) exit
i = i+1
end do
if (i < j) then
! exchange A(i) and A(j)
temp = A(i)
A(i) = A(j)
A(j) = temp
elseif (i == j) then
marker = i+1
return
else
marker = i
return
endif
end do
end procedure partition

end submodule qsort_s

$ cat drive.F90
!Based on a program found at:
!http://www.fortran.com/qsort_c.f95

program drive
use qsort_t, only: qsort
implicit none

integer, parameter :: r = 10
real, dimension(1:r) :: myarray = & ! (1:r)
(/0, 50, 20, 25, 90, 10, 5, 1, 99, 75/)
print *, "myarray is ", myarray
call qsort(myarray)
print *, "sorted array is ", myarray

end program drive

$ cat Makefile

TYPE=real
ifneq ($(TYPE),real)
DT="-Dreal=$(TYPE)"
endif

all: qsort_t.o qsort_s.o drive.o \
drive
@echo done > /dev/null

qsort_t.o:
gfc $(DT) -c -O0 qsort_t.F90 -o qsort_t.o

qsort_s.o: qsort_t.o
gfc -c -O0 qsort_s.F90 -o qsort_s.o

drive.o: qsort_t.o qsort_s.o
gfc $(DT) -c -O0 drive.F90 -o drive.o

drive: qsort_t.o qsort_s.o drive.o
gfc -O0 qsort_t.o qsort_s.o drive.o -o drive

check: \
drive
./drive

clean:
rm -rf qsort_t.o qsort_s.o drive.o
rm -rf drive
rm -rf *mod

$ make check TYPE=integer

Ev. Drikos

unread,
Mar 5, 2018, 12:14:05 PM3/5/18
to
On 05/03/2018 17:20, Ev. Drikos wrote:
> ... Is
> there a better way to reuse code with this example?
>

Now that I'm thinking again about it, I see that the solution described
by FortranFan applies smoothly in this case also.

Ev. Drikos

James Van Buskirk

unread,
Mar 6, 2018, 4:36:02 PM3/6/18
to
"Bálint Aradi" wrote in message
news:47758067-a519-4e5b...@googlegroups.com...
But you don't need an INCLUDE file for every generic function.
In C++ you can have a single template file for each template class
and in Fortran that's often possible as well. In FortranFan's
implementation the contents of an INCLUDE file were the innards
of a single procedure but I consider that to be backwards and
certainly at odds with the C++ template style. I would have one
INCLUDE file for all the procedures and the procedures would
then pick up their types via host association from the module
in which they were contained.

That does require a renaming module if you need to make the
specific procedures accessible to users, for example if they needed
to use a template procedure as an actual argument or invoke one
using sequence association. But I don't like the idea of having
INCLUDE files that are just edits and not full procedures.

Bálint Aradi

unread,
Mar 7, 2018, 6:22:56 AM3/7/18
to
@James: That sounds interesting. Would that be possible that you give an example for it? How would one have to do this, when a generic procedure should be implemented for let's say real(sp), real(dp), complex(sp), complex(dp) and integer? I see, how you could pick up derived type names by host association, but not intrinsic types.

And yes, I agree, I have also some reservations about having non-self containing code pieces in include files. Then, I'd prefer macros more.

Best, Bálint

FortranFan

unread,
Mar 7, 2018, 12:39:11 PM3/7/18
to
On Tuesday, March 6, 2018 at 4:36:02 PM UTC-5, James Van Buskirk wrote:

> ..
> But you don't need an INCLUDE file for every generic function.
> In C++ you can have a single template file for each template class
> and in Fortran that's often possible as well. In FortranFan's
> implementation the contents of an INCLUDE file were the innards
> of a single procedure but I consider that to be backwards and
> certainly at odds with the C++ template style. I would have one
> INCLUDE file for all the procedures and the procedures would
> then pick up their types via host association from the module
> in which they were contained.
>
> That does require a renaming module if you need to make the
> specific procedures accessible to users, for example if they needed
> to use a template procedure as an actual argument or invoke one
> using sequence association. But I don't like the idea of having
> INCLUDE files that are just edits and not full procedures.


As mentioned in another response to this comment, I would like to see some examples as well. We all can then see for ourselves what is backwards, forwards, or going in a full circle.

FortranFan

unread,
Mar 7, 2018, 12:59:48 PM3/7/18
to
On Sunday, March 4, 2018 at 11:30:07 PM UTC-5, Bálint Aradi wrote:

> ..
> Sure, include is a nice standard conforming solution as long as you have to implement one single generic function only. But if you have a lots of them as in a library, I'd prefer simple preprocessor-loops as shown above over having to have a separate include file for every generic function, especially if some of them are only few-liners. But neither solution is optimal, and it is matter of taste, which one you dislike less.
> ..

Huh, why "include is nice .. as long as .. implement one single generic function only"?

A preference, such as for "preprocessor-loops", in a library of many functions is a separate matter altogether. One can have a preference for one or more of a number of other available computing approaches and choose them over Fortran.

But if one is coding in Fortran, then it's *not* just about taste - preprocessing directives are non-standard.

James Van Buskirk

unread,
Mar 8, 2018, 5:47:32 AM3/8/18
to
"Bálint Aradi" wrote in message
news:c18f3e13-694f-4121...@googlegroups.com...
Yeah, that's a problem because you can declare
type(X) y
and if X got it identity from
use(something), X=>T
Then X can be any user-defined type, but there is no way in
the language for T to be INTEGER even though you could
declare
type(INTEGER) y
so the best you can do is to use implicit typing to transmit
the template type if you want to mix intrinsic and derived
types. Some on this forum have said that my example that
did this was so hateful that they recommended that
programmers wait until they are dead for the standard to
catch up before they follow my example with implicit
typing.

Implicit typing has limitations such as not being able to
use it to declare components of a derived type, but it's
better than nothing. Once there is a single preprocessor
line in an entire project everyone involved in the whole
project has to take it into account with the outcome that
effectively it becomes a C project rather than a Fortran
one.

I have posted many examples of generic programming in
this forum and I use to have a generic BLAS implementation
on my web page before it was destroyed by vandals and
thieves. Maybe it's archived somewhere; I don't have the
energy these days to try to resurrect it.

Message has been deleted

FortranFan

unread,
Mar 8, 2018, 10:12:25 AM3/8/18
to
On Thursday, March 8, 2018 at 5:47:32 AM UTC-5, James Van Buskirk wrote:

> ..
>
> Implicit typing has limitations such as not being able to
> use it to declare components of a derived type, but it's
> better than nothing.

> .. Once there is a single preprocessor
> line in an entire project everyone involved in the whole
> project has to take it into account with the outcome that
> effectively it becomes a C project rather than a Fortran
> one.
>
> I have posted many examples .. don't have the
> energy these days to try to resurrect it.


So the readers can presume no examples or links as to what is not "backwards" will be available any time soon, it at all ever.

Clearly Fortran has limitations with generic programming and thankfully there appears to be a recognition and acceptance of the need to improve upon this in the next standard revision.

OP has not stated what coding aspects lead to the use of preprocessing directives, only that they "can be avoided (with extra effort)".

If it's indeed generic programming for interest, then that "extra effort" can be with the need for "interface duplication" but not on "code" duplication. By "code" duplication, I mean the core technical contents, algorithms, etc. And this was what I showed with my snippet upthread with the INCLUDE facility. There is nothing "backwards" about it, contrary to any claims otherwise by @James Van Buskirk, etc. Rather it's just what the standard supports at present:
https://groups.google.com/d/msg/comp.lang.fortran/96tohCrlwV0/Mni-iSrlAgAJ

And given current limitations of the Fortran standard in terms of generic programming, any effort to get around avoid this "interface duplication" will result in the use of something non-standard such as preprocessing directives. Or worse, IMPLICIT TYPING and (quite likely lots of) "clever" programming that only a few can handle!

James Van Buskirk

unread,
Mar 8, 2018, 1:31:40 PM3/8/18
to


"FortranFan" wrote in message
news:cd3027bf-827b-4c7a...@googlegroups.com...

> If it's indeed generic programming for interest, then that
> "extra effort" can be with the need for "interface duplication"
> but not on code duplication. And this was what I showed with
> my snippet upthread with the INCLUDE facility. There is
> nothing "backwards" about it, contrary to any claims otherwise
> by @James Van Buskirk, etc. Rather it's just what the standard
> supports at present:
> https://groups.google.com/d/msg/comp.lang.fortran/96tohCrlwV0/Mni-iSrlAgAJ

I have fixed the code you posted upthread. You're welcome.

D:\gfortran\clf\mylib>type myroutine.i90
subroutine myroutine_template(arg)
real(wp), intent(inout) :: arg
write(*,'(*(g0))') 'In myroutine. KIND = ',kind(arg),', arg = ',arg
end subroutine myroutine_template

D:\gfortran\clf\mylib>type FF_test.f90
module mylibrary_real
use ISO_FORTRAN_ENV, only: wp=>REAL32
implicit none
private
public myroutine
interface myroutine
module procedure myroutine_template
end interface
contains
include 'myroutine.i90'
end module mylibrary_real

module mylibrary_dreal
use ISO_FORTRAN_ENV, only: wp=>REAL64
implicit none
private
public myroutine
interface myroutine
module procedure myroutine_template
end interface
contains
include 'myroutine.i90'
end module mylibrary_dreal

module mylibrary
use mylibrary_real
use mylibrary_dreal
end module mylibrary

program Fsquared
use mylibrary
implicit none
real :: x = log10(2.0)
double precision :: y = 4*atan(1.0)
call myroutine(x)
call myroutine(y)
end program Fsquared

D:\gfortran\clf\mylib>ifort /nologo FF_test.f90

D:\gfortran\clf\mylib>FF_test
In myroutine. KIND = 4, arg = .3010300
In myroutine. KIND = 8, arg = 3.141592741012573

FortranFan

unread,
Mar 8, 2018, 4:20:48 PM3/8/18
to
On Thursday, March 8, 2018 at 1:31:40 PM UTC-5, James Van Buskirk wrote:

> ..
> I have fixed the code you posted upthread. You're welcome. ..

@James Van Buskirk,

You've fixed diddly squat. All you have done is replaced one form of duplication with another:
* you've brought in 2 modules which are effectively copies of each other,
* introduced an additional interface module in the process,
* and also added more lines of code

thus offered up a solution that may even be more complicated by some measure of complexity, especially considering the capabilities with present-day IDEs and how easy it is to work with INCLUDE sections in code.

C:\temp>type myroutine.i90
write(*,'(*(g0))') 'In myroutine. KIND = ',kind(arg),', arg = ',arg

C:\temp>type p.f90
module mylibrary
use ISO_FORTRAN_ENV, only: REAL32, REAL64
implicit none
interface myroutine
module procedure myroutine_real
module procedure myroutine_dreal
end interface
contains
subroutine myroutine_real(arg)
real(REAL32), intent(inout) :: arg
include 'myroutine.i90'
end subroutine myroutine_real
subroutine myroutine_dreal(arg)
real(REAL64), intent(inout) :: arg
include 'myroutine.i90'
end subroutine myroutine_dreal
end module mylibrary
program Fsquared
use mylibrary
implicit none
real :: x = log10(2.0)
double precision :: y = 4*atan(1.0)
call myroutine(x)
call myroutine(y)
end program Fsquared

C:\temp>gfortran p.f90 -o p.exe

C:\temp>p.exe
In myroutine. KIND = 4, arg = 0.301030010
In myroutine. KIND = 8, arg = 3.1415927410125732


If you and other coders like to work with the duplicated modules approach you show, "go ahead, make my day", for it's standard-conforming at least and it does *not* involve IMPLICIT TYPING nor the use of TRANSFER! But as far as I'm concerned, you're trying to put lipstick on a pig, no welcome!

But the larger shame in all this lies with the committees who've worked on the standard since Fortran 90. For such needs have been known and communicated since then and the revisions of 95, 2003, 2008, and now 2018 have passed and the enhanced support for generics in the language is still pending.

Ev. Drikos

unread,
Mar 8, 2018, 4:52:15 PM3/8/18
to
On 08/03/2018 20:31, James Van Buskirk wrote:
> ...
> I have fixed the code you posted upthread. You're welcome.
>
> D:\gfortran\clf\mylib>type myroutine.i90
> subroutine myroutine_template(arg)
>   real(wp), intent(inout) :: arg
>   write(*,'(*(g0))') 'In myroutine. KIND = ',kind(arg),', arg = ',arg
> end subroutine myroutine_template
> ...
>

Hello (hope I don't interfere),

Is this extensible to integers?

Regards,
Ev. Drikos

FortranFan

unread,
Mar 8, 2018, 7:18:51 PM3/8/18
to
On Thursday, March 8, 2018 at 4:52:15 PM UTC-5, Ev. Drikos wrote:

> ..
> Is this extensible to integers? ..


@Ev. Drikos,

Why not try it out yourself, you know more than enough Fortran to do so by now.

Or better yet, take the quick sort case from Rew per the Cormen et al. algorithm and put together a generic solution that works with REAL and INTEGER arrays of different kinds with the duplicated modules approach and see where it gets you.

James Van Buskirk

unread,
Mar 8, 2018, 10:15:41 PM3/8/18
to
"FortranFan" wrote in message
news:c877219f-1dcc-4cdc...@googlegroups.com...
Oh, do I hafta?

D:\gfortran\clf\mylib>type hsort_template.i90
! hsort_template.i90
subroutine hsort_template(Q)
dimension Q(:)
integer left, right
integer current
integer i, j
do i = 2, size(Q)
current = i
j = current/2
do while(j > 0)
if(Q(current)<=Q(j)) exit
Q([current,j]) = Q([j,current])
current = j
j = current/2
end do
end do
do i = size(Q), 2, -1
Q([1,i]) = Q([i,1])
current = 1
left = 2*current
do while(left < i)
right = left+1
if(right < i) then
if(.NOT.Q(right)<=Q(left)) left = right
end if
if(Q(left)<=Q(current)) exit
Q([current,left]) = Q([left,current])
current = left
left = 2*current
end do
end do
end subroutine hsort_template

D:\gfortran\clf\mylib>type FF_t2.f90
! FF_t2.f90
module Tmod
implicit none
private
public T
type T
integer key
character payload
contains
procedure lessneq
generic :: operator(<=) => lessneq
end type T
contains
function lessneq(x,y)
logical lessneq
class(T), intent(IN) :: x
type(T), intent(IN) :: y
lessneq = x%key <= y%key
end function lessneq
end module Tmod

module sortT
use Tmod
implicit type(T)(Q)
private
public hsort
interface hsort
module procedure hsort_template
end interface hsort
contains
include 'hsort_template.i90'
end module sortT

module sortI
implicit integer(Q)
private
public hsort
interface hsort
module procedure hsort_template
end interface hsort
contains
include 'hsort_template.i90'
end module sortI

module sortR
implicit real(Q)
private
public hsort
interface hsort
module procedure hsort_template
end interface hsort
contains
include 'hsort_template.i90'
end module sortR

program FF_t2
use Tmod
use sortT
use sortI
use sortR
real, allocatable :: A(:)
integer, allocatable :: I(:)
type(T), allocatable :: OohAhh(:)
integer N
N = 21
call random_seed()
allocate(A(N),OohAhh(N))
call random_number(A)
I = [(j,j=1,N)]
OohAhh%key = I
OohAhh%payload = transfer('Mission accomplished!',['x'])
do j = 1, N
k = A(j)*(j-1)+1
if(k < j) I([k,j]) = I([j,k])
end do
OohAhh = OohAhh(I)
I = [(j,j=1,N)]
call random_number(A)
do j = 1, N
k = A(j)*(j-1)+1
if(k < j) I([k,j]) = I([j,k])
end do
call random_number(A)
write(*,'(a)') 'Before sorting'
do j = 1, N
write(*,'(i2,f9.6,i3,a2)') j, A(j), I(j), OohAhh(j)%payload
end do
call hsort(A)
call hsort(I)
call hsort(OohAhh)
write(*,'(/a)') 'After sorting'
do j = 1, N
write(*,'(i2,f9.6,i3,a2)') j, A(j), I(j), OohAhh(j)%payload
end do
end program FF_t2

D:\gfortran\clf\mylib>ifort /nologo /assume:realloc_lhs FF_t2.f90

D:\gfortran\clf\mylib>FF_t2
Before sorting
1 0.473186 13 e
2 0.445478 21
3 0.612469 15 n
4 0.018872 20 d
5 0.256149 10 !
6 0.983976 5 M
7 0.102357 4 s
8 0.130658 14 i
9 0.956684 1 c
10 0.695038 19 p
11 0.331626 7 s
12 0.894279 3 o
13 0.014384 12 i
14 0.446544 16 h
15 0.168481 6 c
16 0.120419 11 a
17 0.362859 9 m
18 0.186739 8 i
19 0.122034 2 l
20 0.253218 17 o
21 0.826141 18 s

After sorting
1 0.014384 1 M
2 0.018872 2 i
3 0.102357 3 s
4 0.120419 4 s
5 0.122034 5 i
6 0.130658 6 o
7 0.168481 7 n
8 0.186739 8
9 0.253218 9 a
10 0.256149 10 c
11 0.331626 11 c
12 0.362859 12 o
13 0.445478 13 m
14 0.446544 14 p
15 0.473186 15 l
16 0.612469 16 i
17 0.695038 17 s
18 0.826141 18 h
19 0.894279 19 e
20 0.956684 20 d
21 0.983976 21 !

FortranFan

unread,
Mar 9, 2018, 9:05:59 AM3/9/18
to
On Thursday, March 8, 2018 at 10:15:41 PM UTC-5, James Van Buskirk wrote:

> ..
> Oh, do I hafta?
>


Nah.

It was for the other poster to try out the T*K + 1 (where T is the number of TYPEs to be supported and K is the number of KINDs for each type) modules approach for a generic sorting subprogram. Anyways, you only show for that poster the generic that supports default reals and integers and you left out the module that merges the specific ones. Not a big deal, readers long got the idea. Just that looking at it holistically it's more backwards, especially now with IMPLICIT TYPING, than the alternative.

Nonetheless, one can hope Fortran 202X will address such gaps in the language.

Ev. Drikos

unread,
Mar 9, 2018, 10:24:51 AM3/9/18
to
On 09/03/2018 16:05, FortranFan wrote:
> On Thursday, March 8, 2018 at 10:15:41 PM UTC-5, James Van Buskirk wrote:
>> ...
>
> It was for the other poster to try out the T*K + 1 (where T is the number of TYPEs to be supported and K is the number of KINDs for each type) modules approach for a generic sorting subprogram ...
>
> Nonetheless, one can hope Fortran 202X will address such gaps in the language.
>

Just for the record, in C++ that supports templates the implementation
of the quicksort algorithm is indeed simpler, no code duplication:
http://www.cplusplus.com/forum/beginner/200039/

The solution implemented by J. V. Buskirk answers (also) my question.

Thanks both,
Ev. Drikos

FortranFan

unread,
Mar 9, 2018, 5:57:18 PM3/9/18
to
On Friday, March 9, 2018 at 10:24:51 AM UTC-5, Ev. Drikos wrote:

> ..
> Just for the record, in C++ that supports templates the implementation
> of the quicksort algorithm is indeed simpler, no code duplication:
> ..


Readers will note if brevity and no code duplication (or at least limited) is desired, perhaps with the notion it implies simplicity, the unlimited polymorphic facility in Fortran with CLASS(*) can be made use of, provided an implementation is put together for some generic operators <=, >=. etc. and toward a generic SWAP method. It is trivial effort, moreover it can one-time too.

module QsortC_m
! Based on http://www.fortran.com/qsort_c.f95
! that makes use of Cormen et al., Introduction to Algorithms

implicit none

private

public :: QsortC

contains

recursive subroutine QsortC( A )

! argument list
class(*), intent(inout) :: A(:)

! local variables
integer :: iq

iq = 0
if ( size(A) > 1 ) then
call Partition(A, iq)
call QsortC( A(:iq-1) )
call QsortC( A(iq:) )
end if

return

end subroutine QsortC

subroutine Partition(A, marker)

! Elided is the availability of generic operators and swap
! that takes dummy unlimited polymorphic arguments, say as in
! use xx, only : operator (>=), operator (<=), swap

! argument list
class(*), intent(inout) :: A(:)
integer, intent(out) :: marker

! local variables
integer :: i
integer :: j

i = 0
j = size(A) + 1

do
j = j-1
do
if (A(j) <= A(1)) exit !<-- make use of generic comparison
j = j-1
end do
i = i+1
do
if (A(i) >= A(1)) exit !<-- make use of generic comparison
i = i+1
end do
if (i < j) then
! exchange A(i) and A(j)
call swap( A(i), A(j) ) !<-- make use of generic swap
else if (i == j) then
marker = i+1
return
else
marker = i
return
end if
end do

return

end subroutine Partition

end module QsortC_m


Not saying the above is optimal or efficient, just that it's another option. What's really needed is for Fortran 202X to provide a robust solution for generics that works with data types efficiently.

Anyways with the above, I can write a unit test such as

program p
use QsortC_m, only : QsortC
implicit none
! local variables
character(len=*), parameter :: fmtg="(*(g0.3,1x))"
integer, parameter :: r = 6

blk_1: block
integer :: x(r)
print fmtg, "Block 1"
x = [ 42, 999, -1, 0, 50, 12 ]
print fmtg, "initial array is ", x
call QsortC(x)
print fmtg, "sorted array is ", x
end block blk_1

print *

blk_2: block
double precision :: x(r)
print fmtg, "Block 2"
call random_number( x )
x = real( int(x*100.0D0), kind=kind(x) )
print fmtg, "initial array is ", x
call QsortC(x)
print fmtg, "sorted array is ", x
end block blk_2

print *

blk_3: block
character(len=1) :: x(r)
print fmtg, "Block 3"
x = [ character(len=1) :: "q", "w", "e", "r", "t", "y" ]
print fmtg, "Initial array is ", x
call QsortC(x)
print fmtg, "sorted array is ", x
end block blk_3

stop

end program p

and upon execution with either Intel Fortran or gfortran, get output such as:

Block 1
initial array is 42 999 -1 0 50 12
sorted array is -1 0 12 42 50 999

Block 2
initial array is 88.0 68.0 96.0 91.0 37.0 39.0
sorted array is 37.0 39.0 68.0 88.0 91.0 96.0

Block 3
Initial array is q w e r t y
sorted array is e q r t w y


James Van Buskirk

unread,
Mar 9, 2018, 7:50:58 PM3/9/18
to
"FortranFan" wrote in message
news:bcaf65e4-e0ff-4cfe...@googlegroups.com...

> ! Elided is the availability of generic operators and swap
> ! that takes dummy unlimited polymorphic arguments, say as in
> ! use xx, only : operator (>=), operator (<=), swap

Yeah, I bet that was elided.

Bálint Aradi

unread,
Mar 10, 2018, 7:55:37 AM3/10/18
to
> ! Elided is the availability of generic operators and swap
> ! that takes dummy unlimited polymorphic arguments, say as in
> ! use xx, only : operator (>=), operator (<=), swap

Dear FortranFan, could you post your solution for those generic routines?

Because in my opinion, this is exactly the problematic part. If I had to write those routines, I'd use 'select type' constructs having one 'type is' branch for each type I wish to use. But then again, I were back again to dumb code duplication.

Additionally, if your sort algorithm would be a library routine, the generic operators would have to be known already when you compile the library, so users of the library could not really extend it. Alternatively, one could have some complicated mechanism with procedure pointers which allows users to register or pass such functions to the sorting routine, but that seems to be somewhat an overkill to me.

Finally, by using class(*) arguments in your sort routine, you basically switch off type checking at compile time, so the compiler would not recognize, if one calls the sort algorithm with a type which is not handled in the generic operators. Of course, it can be caught at run-time, but it I prefer to let the compiler check for type correctness. By using preprocessor macros, you wouldn't have code duplication, but could keep type checking at compile time.

But maybe I am overlooking some smart solution, so I'd be interested in your implementation for those generic routines.

Coming back to your earlier comment about preprocessor directives not being standard: Sure, they are not. But I consider the preprocessor as part of the build system and not as part of the compiler. Our preprocessor (Fypp) is one single python file which we distribute along with the Fortran sources, so we can be sure, the Fortran code coming out of the preprocessor is the same on all architectures and independent of the compiler being used. Not an optimal solution, but I still prefer it over mechanic code duplication.

And last but not least: Your suggestion to choose a different language if templates are important, may not always be realized. Especially, if you want to write a library to be used by Fortran programs, passing Fortran specific constructs, like derived types, polymorphic objects, allocatable variables etc. around. Even with the newest C interoperability constructs, it would be hard to write such libraries in any other language as Fortran itself.

Best, Bálint

FortranFan

unread,
Mar 10, 2018, 5:49:01 PM3/10/18
to
On Saturday, March 10, 2018 at 7:55:37 AM UTC-5, Bálint Aradi wrote:

> > ! Elided is the availability of generic operators and swap
> > ! that takes dummy unlimited polymorphic arguments, say as in
> > ! use xx, only : operator (>=), operator (<=), swap
>
> Dear FortranFan, could you post your solution for those generic routines?
>
> Because in my opinion, this is exactly the problematic part. If I had to write those routines, I'd use 'select type' constructs having one 'type is' branch for each type I wish to use. But then again, I were back again to dumb code duplication.
> ..


@Bálint Aradi,

There is *no* "smart solution" underneath, it's as you say SELECT TYPE constructs with a case for each supported type and its kind. As I wrote it's trivial effort.

select type ( a )
type is ( xxx )
select type ( b ) ; type is ( xxx )
! simple operation involving a and b such as <, >, and =
class default
! error handling
end select
..


As discussed here and elsewhere, there is no denying the fact Fortran has certain gaps in its support for generic programming. The case at hand involving a quick sort algorithm is a textbook example of this issue.

Your points all lead toward a good rationale for improvements in the language, especially because the standard-bearers of Fortran, unlike those for C, long decided not to leave the language in maintenance mode.

Given current gaps in the Fortran standard, coders have to bring in some facility outside the standard such as preprocessing or forgo something or the other. The former is non-standard and under the circumstances, the teams I work with or I simply will *not* use it. Now say you make your Fypp solution part of IEC ISO standard, then that can change everything.

Note I waited to suggest the unlimited polymorphic dummy argument (CLASS(*)) option precisely because, as you say, compile-time type-checking is then sacrificed; this is in addition to other concerns with such an approach including compiler bugs and optimization. As I wrote, it is not optimal. About the only benefit is one can condense the code duplication to a few methods: a couple of comparison (less than and greater than) operators and toward assignment. The higher-level algorithms such as the quick sort one can then be spared from duplication.

This can be a "good enough" *standard-conforming* option though in some applications and teams and domains i.e., at least until the Fortran 202X solution becomes available.
0 new messages