- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In the Mixed Language Programming chapter, look at Legacy Extensions, ATTRIBUTES. There is a detailed table showing the various calling conventions under that heading.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page