Weird behavior on format string with carriage return

This is an issue that I’ve easily fixed, but I’m just curious to know whether that’s gfortran-specific, or its rendered with any compilers.

If I add a carriage return to a format string, followed by an arbitrary-sized loop, I’m forming what
would be opening a comment section in C/C++: /*
Everything compiles just fine if I run gfortran with no flags, but if I turn on the C preprocessor, I get an error:

# gfortran.exe -cpp test_dynamic_formats.f90
test_dynamic_formats.f90:11:0:

   11 |    1 format('my long array is: '/*(1x,1pe12.3e2))
      |
Error: unterminated comment

Am I doing some illegal formatting, or it’s just the compiler pre-processor getting confused with valid input that’s similar to C-style comments?

program test_dynamic_format

   integer, parameter :: N = 100
   real :: x(N)

   call random_number(x)
   print 1,x
   print 2,x

   ! Crashes with -cpp due to "comment"
   1 format('my long array is: '/*(1x,1pe12.3e2))

   ! Works
   2 format('with verbose format: ',/(*(1x,1pe12.3e2)))

end program test_dynamic_format

The C preprocessor has no knowledge of Fortran syntax. You will probably find that it works correctly if you replace the quotes (’) by apostrophes (") as they delimit literal strngs in both C and Fortran.

Uhm, that’s not the case apparently:

test_dynamic_formats.f90:11:0:

   11 |    1 format("my long array is: "/*(1x,1pe12.3e2))
      |
Error: unterminated comment

It seems like the C preprocessor must be removing all comments from the code as the very first thing, so it doesn’t know that its a valid Fortran command yet

Oops, I was thinking too much of embedded formats. Something along these lines ought to work:

write(*,"('my long array is: '/*(1x,1pe12.3e2)") ...

Then the entire format string appears as a literal string in the C way.

Interesting. I can never decide whether old-style format with label is better than using a string, this is one example where using a string is definitely safer. I would use something like:

character(*), parameter :: fmt_1 = "('my long array is: '/*(1x,1pe12.3e2)"

That tends to be far more verbose than label-based format though (not that C or C++ have better ways to specify formatted output either, Fortran’s is not so bad IMHO)

Well, I am prejudiced of course, but a real shortcoming in my view of C/C++ format strings is that you have no way for specifying repetition counts (at least not that I know of) and as a consequence no way of grouping things. Besides a bunch of other quirks :slight_smile:

There are a handful of “gotchas” like this when using the C preprocessor on fortran codes. Most of them involve C comments or trigraph sequences. They can usually all be fixed by making minimal changes to the fortran source, but you sometimes end up with odd-looking fortran code. In the above case, just add a space or a comma between the / and the *, and that should work. These could easily be fixed if the fortran committee would standardize the C preprocessor syntax, but for some unknown reason, they have refused to do this for the last 30+ years. So add the space or add the comma, maybe leave a comment in the code explaining why it is there, and move on to worry about more important things.

Yes; but would just add that you can often see what the preprocessor has done by running it by itself.

If you are using something like make(1) or cmake(1) to build your code you might want to do the preprocessing as a separate step so you can use switches like -traditional, -C, -P … but the cpp(1) command varies between platforms, so if you use a lot of platforms that can be an issue.

The Intel compilers ( among others) come with an fpp(1) preprocessor just because of these issues. fpp is a common name for a version of cpp that is “Fortrn safe” but varies between vendors. unfortunately gfortran(1) does not supply a “fpp”. You can say to just see the post-processed lines instead of compiling them by entering

gfortran -E -cpp source.F90

which can sometimes help figure out what happened.

A comma between the “/” and the “*” also works

  1 format('my long array is: '/,*(1x,1pe12.3e2))

It is that kind of “fortran safe” preprocessor that needs to be standardized. Fortran programmers have used the C preprocessor as a de facto standard since the 1980s. It should have been standardized long ago.

1 Like

One good reason for using strings instead of FORMAT statements is that you don’t need numbered labels. Whenever I see one I always have to hunt for GOTO 666 and ERR=666 and END=666 and loops beginning DO 666 N=1,1000 etc. and look up the standards back to f66 (because it might be an old program) to see if 666 is now or ever was the label of a statement that is a valid one to go to.

CoCo was a new pre-processing language. Ron is suggesting (and I agree) that the existing practice of cpp-like pre-processing be standardized. That’s what is widely used.

The community could create a standard for Fortran pre-processing, but it would be hard to get commercial compilers to adopt it.

1 Like

Well, Fortran survival is bound to it’s C interoperability IMHO, to me, standardized compatibility with the C preprocessor would be the way to go today

The LLVM Flang project documented much of the preprocessor behavior in current Fortran compilers and made recommendations on how preprocessing could be made more standard.

Look at Preprocessing.md in the Flang source.

We (I was at NVIDIA at the time) tried standardizing preprocessor behavior up to the Fortran standards committee. I recall that there was less than no interest in addressing this.

I still wish the standards committee would do something about this, as it is directly related to the portability of user code between compilers.

Right, @kargl. I wasn’t involved with Fortran at that time, but should have mentioned CoCo.

Did any of the Fortran vendors implement it? Or use Dan’s implementation?

I’m not aware of any, but that may just be ignorance on my part.

Thinking of “surprises” from pre-processing, pre-defined macros are very compiler-specific and another
cause of issues other than C and C++ comments, etc. With ifort try putting this into “testit.F90” and
compiling it:

program testit
linux=10
end program testit

Hint: “linux” is predefined in ifort!

I think the new interest in preprocessing is at least partly driven by a recent upswing in people looking at ways to do templating. At least for me, support of ISO_C_BINDING interfaces has eliminated a lot of the “classic” reasons I needed preprocessing.

Try removing the -cpp flag or explicitly replacing it with -nocpp. If you are using CMake+Ninja, for some reason, it adds the -E option, which does not allow the -nocpp to be used. In that case, use the below line:

set_source_files_properties(<fileName.f> PROPERTIES Fortran_PREPROCESS OFF)

The /* case is interesting because the problem I always had was //. Both begin comments in C/C++. The latter is string concatenation in Fortran, and really easy to trip over when using a non-Fortran-aware cpp. I wasn’t aware of a similar problem with /*. Splitting with a comma would be a good work-around.

I played with Dan’s version of co-co years ago, because it supported macros. However these days I tend to use fypp for my personal projects.