Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Passing strings, C and Fortran 11.x vs 12.x differences?

jkwi
Beginner
1,023 Views

  Behaviour of passing strings between C and Fortran is changing?
  Following code gives different results between 11.x and
  12.x versions if icc/ifort.  C main calling Fortran sub passing in strings/ints.

  Maybe 11.x was letting something slide that wasn't legit.
  I know there are C bindings now in Fortran but this is
  old code that I've stripped down to basics
  Would character(len=1)  array(N) be the answer?

  ifort/icc 11.1 20100806  has no problem with this.
  ifort/icc 12.1.5 20120612 gives in mainfort:

Address of i2a           7FFFD6815FA8
 i2a           22
Address of rseed            7FFFD6815FA0
 rseed           -1
Address of f1c            7FFFD6815F00
forrtl: severe (408): fort: (18): Dummy character variable 'F1C' has length 80 which is greater than actual variable length 0

 
top.c

#include <stdio.h>
#include <string.h>

void mainfort_(char f1c[80], char f2a[80],
              int *i1c, int *i2a, int *rseed);

int main (int argc, char *argv[])
{
  char f1c[80],  f2a[80];
  int i1c, i2a;
  int rseed;

  strcpy(f1c, "long string number 1");
  strcpy(f2a, "longer string number 2");

/* This is a print to see if we execute this */
  printf("This is the main in C\n");

  rseed = -1;
  i1c=strlen(f1c);
  i2a=strlen(f2a);

printf("i1c %d\n",i1c);
printf("address of i1c %p\n",&i1c);

printf("i2a %d\n",i2a);
printf("address of i2a %p\n",&i2a);

printf("rseed %d\n",rseed);
printf("address of rseed %p\n",&rseed);

printf("address of f1c, f2a %p, %p\n",&f1c,&f2a);
printf("%s %s\n",f1c,f2a);


  mainfort_(f1c, f2a, &i1c, &i2a, &rseed);


}

mainfort.f90
subroutine mainfort(f1c, f2a, i1c, i2a, rseed)
  implicit none
  character*80:: f1c, f2a
  integer :: i1c, i2a, rseed

  print *,"In mainfort---"
  print "(A,Z)", "Address of i1c ",loc(i1c)
  print *, "i1c ",i1c

  print "(A,Z)", "Address of i2a",loc(i2a)
  print *, "i2a ",i2a

  print "(A,Z)", "Address of rseed ",loc(rseed)
  print *, "rseed ",rseed

  print "(A,Z)", "Address of f1c ",loc(f1c)
  print*, 'f1c  ', f1c(1:i1c)
 
  print "(A,Z)", "Address of f2a ",loc(f2a)
  print*, 'f2a  ', f2a(1:i2a)

 print "(A,Z)", "Address of rseed ",loc(rseed)
 print *, "rseed ",rseed


end subroutine mainfort

0 Kudos
13 Replies
Steven_L_Intel1
Employee
1,023 Views

The passing didn't change - what did change was that we added a runtime check to make sure that the passed character length was at least as large as the declared length. You didn't pass the length (where Fortran could see it) so it picked up whatever was on the stack at the time.

The fix for this is to add:

!DEC$ ATTRIBUTES REFERENCE :: f1c,f2a

to prevent it from looking for the length.

0 Kudos
jkwi
Beginner
1,023 Views

 Could you point me to a reference or give an example of how to make the call so Fortran can see the length?

 In the fortran code if I change the character*80  f1c   character(len=1)  f1c(80) it works. Not sure I understand why.

0 Kudos
mecej4
Honored Contributor III
1,023 Views

There is an entire chapter on mixed language programming in the Intel Fortran Compiler User Guide. If your work requires passing strings between Fortran anc C, you need to read the chapter. There are many different ways of arranging the inter-operability, and it is possible for faulty code to work correctly for some time, as you have discovered.

When things don't work, a look at the assembly listing will show you what goes wrong, if you know assembly well enough and understand the calling conventions. Otherwise, you must understand the rules at the Fortran level, check that the caller and callee are consistent with regard to number, type and INTENT of arguments.

0 Kudos
Steven_L_Intel1
Employee
1,023 Views

Your change works because making the argument an array disables the run-time check for this case. It is still not correct.

By default, Fortran expects to see address-sized integer lengths of all the character arguments at the end of the argument list, passed by value. These would not be represented by dummy arguments on the Fortran side. If you are using BIND(C) to specify C interoperability, then there are no lengths, but you would then also be required to make any character arguments an array of single characters.

As mecej4 says, there is a large chapter on mixed-language programming in the documentation. There are several approaches you can take. My advice would be to use the Fortran standard features for C interoperability, but you can correct your existing code without too much effort.

0 Kudos
jkwi
Beginner
1,023 Views

  Thanks for the replies.

  I'm looking at the 2013 XE Fortran User and Reference Guide. I looked through the Mixed Language Programming section and it handles the new BIND (C) standards and the legacy extensions. I don't see any references to the 'hidden' passing of lengths at the end of the argument lists. Any reference for that?   I realize the best way to do this is conforming to the standard but we're getting old code in and I want to understand what they were doing.

  Thanks.

0 Kudos
Steven_L_Intel1
Employee
1,023 Views

It's in the page on ATTRIBUTES in the mixed-language chapter.  See here.

0 Kudos
mecej4
Honored Contributor III
1,023 Views

In the Mixed Language Programming chapter, look at Legacy Extensions, ATTRIBUTES. There is a detailed table showing the various calling conventions under that heading.

0 Kudos
jkwi
Beginner
1,023 Views

 I see the line:
By default, Fortran passes all data by reference (except the hidden length argument of strings, which is passed by value).

 Without an example its still a little tricky.

Example at:
http://www.math.utah.edu/software/c-with-fortran.html

  C has the length args, in Fortran len_b and len_a are not
  defined. I can just use the strings B and D as I normally
  would in Fortran and the compiler took care of knowing where
  the ends were?  What if instead of * I set length as 80, as
  long as len_b and len_d were not longer I would be OK?

  Do those len_b and len_d need the '_' in front to denote that they are the hidden lengths?


  I'll play with this tonight.

       SUBROUTINE FOO(A, B, C, D)
       INTEGER A
       CHARACTER*(*) B
       REAL C
       CHARACTER D(*)
       ...
       END

void foo_(int *a, char *b, float *c, char *d,
         int *_len_b, int *_len_d)

 

0 Kudos
Steven_L_Intel1
Employee
1,023 Views

The text I referred to is:

For certain string arguments:

  • Len:End applies when-nomixed-str-len-arg(Linux* OS and OS X*) or/iface:nomixed_str_len_arg(Windows* OS) is set. The length of the string is pushed (by value) on the stack after all of the other arguments. This is the default.

The C code you quote has  (at least)two errors.  The first is the use of int for the lengths - this should be size_t. The second is the * - these lengths are passed by value, not reference.

The _ doesn't mean anything special here.

0 Kudos
jkwi
Beginner
1,023 Views

Sorry to belabor this (too late).  If the lengths come in as values at end, ordered (by default). What am I supposed to do with them in the Fortran routine? 

Say:
subf_(int *ai, char f1, float *cr, char f2, size_t len_f1, size_t len_f2);
char f1[80],f2[80];
size_t len_f1, len_f2;
int ai=10;
float cr=3.14;

subf_(&ai, f1, &cr, f2, len_f1, len_f2)

Fortran:

subroutine subf(a,b,c,d)
integer a
real c
character(80) b,d  ! how do I correctly declare these?  Again will move to BIND(C) eventually but need to figure out this way first.

0 Kudos
mecej4
Honored Contributor III
1,023 Views

Here is your example with enough filled in to compile and run (the name mangling is for Windows, 32-bit compilers).

[cpp] #include <stdio.h> #include <string.h> main(){ extern void SUBF(int *ai, char *f1, float *cr, char *f2, size_t len_f1, size_t len_f2); char f1[80],f2[80]; int ai=10; float cr=3.14; strcpy(f1,"First string"); strcpy(f2,"Second string"); SUBF(&ai, f1, &cr, f2, strlen(f1), strlen(f2)); } [/cpp]

[fortran] subroutine SUBF(ai, f1, cr, f2) integer ai real cr character(*) :: f1,f2 write(*,10)f1,len(f1),f2,len(f2) 10 format(1x,'|',A,'|',T20,I3) return end [/fortran]

0 Kudos
Steven_L_Intel1
Employee
1,023 Views

You don't declare them - they''re "hidden" arguments.  The run-time bounds check will compare them against the 80 you specified for the length.  If you're going to pass the lengths, then use character(*). In fact, I would recommend that all the time unless you're using BIND(C).

0 Kudos
jkwi
Beginner
1,023 Views

OK very helpful. Thanks!!    I'll run tests on this, makes sense now.   I will move on to BIND(C) once I get these working. 

0 Kudos
Reply