0

In Using python-ctypes to interface fortran with python, the answer mentions the following:

In Fortran, arguments are passed by reference. Fortran character arrays aren't null terminated; the length is passed by value as an implicit long int argument.

Is that part of the Fortran standard or a compiler specific implementation?

(If the answer could explain how to search the fortran standard that would be a big plus).

More specifically, I'm interested as to whether the following use of ctypes to call a Fortran string function is valid because of the compiler or because it's part of the Fortran standard.

Here's the example Fortran string function:

 function prnt(s)          ! byref(s), byval(length) [long int,   implicit]
     character(len=*):: s  ! variable length input
     logical :: prnt
     write(*, "(A)") s     ! formatted, to remove initial space
     prnt = .true. end function prnt

Here's how you could call this function with Python's ctypes module:

 >>> from ctypes import *
 >>> test = CDLL('./test.so')
 
 >>> test.prnt_.argtypes = [c_char_p, c_long]
 
 >>> s = 'Mary had a little lamb'
 >>> test.prnt_(s, len(s)) Mary had a little lamb 1

Notice in Fortran, there is only a single argument, the string. But secretly, when a function has a string argument, it appears that a second argument is present which is the length of the string.

Is this something I can assume is true regardless of the compiler because it is part of the Fortran standard?

EMiller
  • 2,792
  • 4
  • 34
  • 55
  • 2
    Given that the statement "In Fortran, arguments are passed by reference." in itself is not true, do you still require the clarification? – francescalus Jun 30 '23 at 20:22
  • 1
    In Fortran we don't usually talk about the "length" of an array, but its _size_ or _shape_. We talk about length of characters in a different way. `character(len=5) str(6)` is an array of size 6 and character length 5, as most people would describe, so could you please be explicit in the cases you are interested in? – francescalus Jun 30 '23 at 20:31
  • @francescalus it used to be true. I remember hearing of an old Fortran compiler that would allow you to modify a constant if you passed it to a function. Good luck debugging that! – Mark Ransom Jun 30 '23 at 20:36
  • 2
    @MarkRansom, that a Fortran compiler may do something seen as amusing when asked to compile something which isn't a Fortran program doesn't seem terribly relevant to whether the Fortran standard mandates that all arguments are passed by reference, or even that the Fortran standard has the slightest view on what "pass by reference" means. – francescalus Jun 30 '23 at 20:47
  • @francescalus what I'm telling you is that Fortran at one time only had pass by reference, and it led to an amusing bug. I have not kept up with it in the last 40 years so it wouldn't surprise me if parameter passing standards have greatly changed. – Mark Ransom Jun 30 '23 at 20:57
  • Yes, @MarkRansom, I'm aware of the existence of compilers which could be lied to and persuaded to modify the value of a constant. Their existence doesn't mean that it's impossible to have a FORTRAN 77 compiler (the earliest language revision which applies to this question) which may implement association between entities in different program units by means other than "pass by reference". – francescalus Jun 30 '23 at 21:28
  • @MarkRansom Did you ever read [Real Programmers Don't Use Pascal](https://www.ecb.torontomu.ca/~elf/hack/realmen.html)? *"The only parameter passing mechanism endorsed by Real Programmers is call-by-value-return, as implemented in the IBM/370 FORTRAN G and H compilers."* Wikipedia also mentions *"Call by copy-restore Fortran IV, Ada[10] 1962"* https://en.wikipedia.org/wiki/Evaluation_strategy This is not something that has changed in the last 40 years. Pass by reference wasn't the only mechanism already at the time when the first FORTRAN 66 standard was being written. – Vladimir F Героям слава Jun 30 '23 at 21:32
  • 1
    @EMiller There is not a single array in the code you showed. Your title should probably be changed just to *"Character arguments in Fortran Functions - Is their length always passed in implicitly?"* – Vladimir F Героям слава Jun 30 '23 at 21:40
  • To add to Vladimir F's comment: not only is the character variable you have a scalar, not an array, it's also a very specific type of character variable. It's an assumed-length character. If that's the only type you're interested in, it would help to be clear. (You'll possibly get a different answer again if you asked about `character(len=3) s`, or `character(len=n) s`.) – francescalus Jun 30 '23 at 21:46

1 Answers1

1

The character string (not array) passing details are not guaranteed by the standard. In practice, when using assumed length character string arguments (len=*) you will always find a hidden argument at the end of the argument list with the length, but you cannot be sure about the details. They are specified by individual compilers. Maybe all compilers on mainstream platforms like x86_64 will have it the same, but there is more in computing world.

And when you use the bind(C) attribute for the interoperability with C, the hidden argument is not used at all, all strings must be length 1 and you must actually use arrays of characters. In fact, you should be using this attribute when calling your procedures from another language, e.g. from Python using ctypes. Then you can control the way of passing the length manually.

A character string: character(len=n) :: str
A character array: character :: str(m)
An array of character strings: character(n) :: str(m)

  • Terribly minor point: although only length-1 characters are interoperable, an assumed/deferred length character may be an argument in an interoperable procedure. – francescalus Jun 30 '23 at 22:19
  • @francescalus Yes I had that suspicion and had a different formulation in the first revision. Then suddenly I became worried that it might be wrong and changed it. – Vladimir F Героям слава Jun 30 '23 at 23:13
  • Until F2018 (or F2008+TS), the only allowed length for a character in an interoperable procedure was 1. Assumed and deferred characters require formal parameters to involve the descriptor, so for all intents with Python, R, etc interop they don't happen. – francescalus Jun 30 '23 at 23:42