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

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

  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

14 帖子 / 0 全新
最新文章
如需更全面地了解编译器优化,请参阅优化注意事项
Steve Lionel (Intel)的头像

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.

Steve

 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.

mecej4的头像

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.

Steve Lionel (Intel)的头像

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.

Steve

  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.

Steve Lionel (Intel)的头像

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

Steve
mecej4的头像

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

 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)

 

Steve Lionel (Intel)的头像

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.

Steve

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.

mecej4的头像

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


#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));

}

 


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

Steve Lionel (Intel)的头像

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

Steve

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. 

登陆并发表评论。