3

I have a problem with passing strings between Fortran and C.

The Fortran subroutine call looks like this:

CALL MMEINITWRAPPER(TRIM(ADJUSTL(PRMTOP)), 0, SALTCON, RGBMAX, CUT)

The C which works with this has the signature:

int mmeinitwrapper_(char *name,
                    int *igb,
                    REAL_T *saltcon,
                    REAL_T *rgbmax1,
                    REAL_T *cutoff1)

I put some print statements in various places and everything works just fine, until I compile with ifort. In that case, the output looks like this:

  Topology file name:
 coords.prmtop                                                                  
   coords.prmtop
 Topology file name length:          81          13
length in C: 8 
read argument: coords.prmtop��* 
Reading parm file (coords.prmtop��*)
coords.prmtop��*, coords.prmtop��*.Z: does not exist
Cannot read parm file coords.prmtop��*

With the Portland compiler:

  Topology file name:
 coords.prmtop                                                                    
 coords.prmtop
 Topology file name length:           81           13
length in C: 8 
read argument: coords.prmtop 
Reading parm file (coords.prmtop)

The lengths in the first set are from Fortran of the untrimmed/unadjusted string and then the trimmed/adjusted string. The length in C is from sizeof(name)/sizeof(name[0]).

It seems to be passing a section of memory that's too long and in subsequent runs you get different lengths of bad stuff written (though the reported length in C is always 8).

Does anyone have any ideas? It's difficult getting gdb to play nicely with the Fortran/C combination.

Kyle_S-C
  • 1,107
  • 1
  • 14
  • 31
  • I don't understand if you are calling fortran code from c code or vice versa? In other words, where is the "*name" string allocated and set before passing to a function (the c interface or the fortran code?) – Sebastien Nov 11 '13 at 18:57
  • Sorry, `*name` is allocated in the Fortran code, which calls the C code. – Kyle_S-C Nov 11 '13 at 19:11
  • What happens if you `CALL MMEINITWRAPPER(TRIM(ADJUSTL(PRMTOP))//c_null_char,...)`, where `c_null_char` is a constant from `use iso_c_binding, only: c_null_char`? – milancurcic Nov 11 '13 at 19:14
  • What code do you have control over? In C, you would expect a "0" character ("\0") at the end of the string to mark the end of word. It seems that the various fortran compiler treats the TRIMM-ed string differently leaving zeros or not in some cases. Could you add the number of characters in the prototype to have the C code strncpy the first characters? – Sebastien Nov 11 '13 at 19:16
  • There is no reason, why Fortran code should add zeroes to strings.It always have to be appended manually. – Vladimir F Героям слава Nov 11 '13 at 19:47
  • In this era it is easier to communicate between Fortran and C using the Fortran ISO_C_Binding instead of deducing the passing mechanisms of a particular pair of compilers. Additionally a solution based on the ISO_C_Binding will be portable. – M. S. B. Nov 11 '13 at 20:42

2 Answers2

5

I believe the answer you're looking for is here:

Arrays of strings in fortran-C bridges using iso_c_binding

Basically, fortran "knows" the length of a string but not C so you have to let the C code know somehow by transmitting the fortran length to C and then reacting appropriately in the C code.

This question below explore this issue from a "pure" fortran POV with some insights in the various answers given:

Fortran to C , effect of trim on space allocated to string

And bear in mind that the compilers might exploit some undefined or implementation-specific differences to explain the varied observed behavior.

Also, I just realized you make a mistake by assuming that sizeof gives the size of the string. It gives the size of the pointer. So sizeof(name)/sizeof(name[0]) is a constant giving the size of a char which itself is a constant of 8 bytes in C. sizeof(name) gives the size of a char pointer and sizeof(name[0]) gives the size of a char. The result is a constant 8.

Community
  • 1
  • 1
Sebastien
  • 1,439
  • 14
  • 27
  • 3
    Also see http://stackoverflow.com/questions/8207997/calling-a-fortran-subroutine-from-c/8208960 – M. S. B. Nov 11 '13 at 20:40
  • 1
    and http://stackoverflow.com/questions/9972743/creating-a-fortran-interface-to-a-c-function-that-returns-a-char – haraldkl Nov 11 '13 at 21:14
2

There are several problems here.

  • sizeof(name) in C returns the size of the pointer called name. I take it you are on a 64 bit platform - hence you always see 8.

  • C often expects string length to be determined by a trailing null sentinel character. The absence of that character can produce strange results. We can't be sure whether that is an issue here without more information.

  • Fortran compilers differ in their passing conventions for the length of a character variable - it may be as an additional thing immediately after a pointer to the character data, or appended to the end of the other arguments. The rules around interoperability of a procedure with the BIND(C) attribute remove the requirement to pass this length as interoperable character variables can only be of length one. We can't be sure whether this is a problem as you don't show an interface block for the subroutine call on the Fortran side.

So... decide on how you are managing string length on the C side (fixed length? terminating null? separately passed length?), adjust the Fortran call appropriately and add an interface block to the Fortran side with BIND(C).

IanH
  • 21,026
  • 2
  • 37
  • 59