Kyle_S-C
Kyle_S-C

Reputation: 1177

Passing string between Fortran and C

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.

Upvotes: 3

Views: 3494

Answers (2)

IanH
IanH

Reputation: 21431

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

Upvotes: 2

Sebastien
Sebastien

Reputation: 1476

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.

Upvotes: 6

Related Questions