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

Problems passing char strings from C++ to FORTRAN on 64bit compile?

Tony_Garratt
Beginner
2,602 Views

Hi,

We have been successfully calling FORTRAN from C++ and passing strings. We are using VC2005 and Intel FORTRAN 10. Everything works fine in 32bit, but when we switch building 64bit, we are getting crashes immediately when the FORTRAN is called from C++. Everything points to the character string being broken - all the arguments in the subroutineargument list beforethe first character arugmentare fine, but the characters themselves and the remaining arguments after the strings are corrupted.

This leads us to conclude that somehow the C++ to Fortran string handling is different in 64bit to 32bit, which is somewhat suprising, or maybe there is a bug in the Intel 64bit implementation or perhaps the lengths of the strings are being passed somewhat differently or in a different order in 64bit than it is in 32bit.

Any advice is most welcome.

Thank you

Dr Tony Garratt
Senior Numerical Analyst

Reaction Design



0 Kudos
14 Replies
Steven_L_Intel1
Employee
2,602 Views
Are you correctly passing a 64-bit length field? Show us the C++ header for the Fortran routine and then the Fortran SUBROUTINE/FUNCTION statement and the declarations of its arguments.

There is no fundamental difference in how strings are passed other than that all argument slots are 64-bits wide.
0 Kudos
TimP
Honored Contributor III
2,602 Views
If you use the cdecl (default) interface, or /iface:cvf, on 32-bit, it ought to work the same on 64-bit. The stdcall interface, which was used by MSVC at the time of CVF, isn't supported by 64-bit Windows.
Your character string length arguments must come at the end of the list, unless using /iface:cvf.
If this isn't sufficient to answer the question, please show an actual example.
0 Kudos
Tony_Garratt
Beginner
2,602 Views

Thank you so much - of course, the answer is obvious now that you point it out (as always with answers to questions!). We are usingstdcall which isnt available for 64bit, so we have to change our code to pass the string lengths at the end of the argument list.

regards,

Tony

0 Kudos
Steven_L_Intel1
Employee
2,602 Views
I'm confused - STDCALL has nothing to do with where string lengths are passed. You can specify STDCALL on Intel 64 but it will be ignored as far as how routines are called. (It still has an effect on the case of routine names and pass-by-value.

It sounds as if you were using CVF conventions on IA-32, which among other things would cause string lengths to be passed after the string address. Did you use the same option on the 64-bit compile? What exactly did you say to get CVF conventions? The "CVF" option for calling convention or explicit STDCALL, NOMIXED_STR_LEN_ARG, REFERENCE attributes?

It may be that the compiler is not putting the string length in the proper position when CVF conventions are requested on x64. I'll have to try that and see.
0 Kudos
Tony_Garratt
Beginner
2,602 Views

Hi Steve,

We were using the STDCALL (CVF calling convention) option in our code - passing the characters as (char*, t_size) pairs, which worked fine on IA-32. We used the same option when compiling 32 and 64bit because we used the same code base.

But the compiler documention for Intel 10 says that STDCALL isnt supported on 64bit (see below help from the iface help page - incidentally we didnt use iface, just put STDCALL in the subroutine declaration) which makes me think we need to change all our code to use the default convention (with the string lengths passed at the end).

What do you think?

Description

This option specifies the default calling convention for an application or the argument-passing convention used for hidden-length character arguments.

On systems using IA-32 architecture, you can change the default calling convention by using one of the following methods:

  • Specify /iface:cref, /iface:cvf, /iface:stdcall, or /iface:stdref

  • Specify the ATTRIBUTES directive (using the C, STDCALL, REFERENCE, or VALUE option) in an explicit interface

The second method overrides the first.

On systems using Intel 64 architecture, you cannot change the default calling convention because only one calling convention exists on those systems.

On On systems using IA-64 architecture, the only option available is /iface:default.

Option Description
/iface:default Tells the compiler to use the default calling conventions. This is the only option available on systems using IA-64 architecture.

0 Kudos
Steven_L_Intel1
Employee
2,602 Views
There is more to "CVF calling convention" than STDCALL. CVF's convention includes all of the following:

  • STDCALL calling mechanism
  • Arguments passed by reference
  • Routine name upcased and decorated with leading underscore and trailing @n
  • String lengths passed immediately following string address
On Intel 64, you CAN specify /iface:cvf (the documentation is wrong and I will report that). There the string length passing is changed, but there is only one calling mechanism and the name decoration is different (upcased, no underscore or @n).

I verified that you can set /iface:cvf on Intel 64 (including in Visual Studio) and that it does indeed change where string lengths are passed. Since there is only one calling mechanism, the "STDCALL" part is ignored, as I assume it would be in C++.

I'm missing part of the story here. Your code should have worked just fine if you used the same project options on Intel 64 as you did on IA-32. All I can think is that either you were using CVF and not Intel Fortran for your IA-32 builds, or that you have different project settings between IA-32 and Intel 64. If you deliberately changed the /iface setting on the 64-bit project because the documentation said it was not supported, then that would cause the problem you saw. You chose the solution I would prefer - making the C code match the Intel defaults, but you could also have used matching project options (including /iface:cvf) for both 32 and 64-bit builds and it should also have worked that way.
0 Kudos
Tony_Garratt
Beginner
2,602 Views

Thanks very much for the clarification.

I have created a small test problem that shows the problem we are having. The test problem is a main C++ progam that calls a simple FORTRAN routine and passes a few ints and 2 characters to the FORTRAN. We use a CHARACTER class that handles the (char *, length value) pairings. This class works fine on win32 and several other platforms (such as linux 32 and 64, AIX etc).

If the code is built with VC2005/Intel 10 in 32bit it works fine as one would expect. If the same code is built with 64bit (no changes apart from switching to 64bit compile options), then the fortran subroutine crashes because the strings are corrupted. This leads me to think that there might be a bugin the Intel 64bit CVF string handling.

I have added the code below but can send you the VC2005 projects (for the C++ and FORTRAN) if you give me an email address.

Any advice is most appreciated.

Thank you!

Tony

// Calling.cpp : Defines the entry point for the console application.

//

#include

"stdafx.h"

#include

#include

#include

class

CHARACTER

{

//

// Trick to force compile-time errors on any attempt to copy or assign.

// Note: DO NOT WRITE THESE, LET THE COMPILER AND LINKER FIND THESE

// ILLEGAL ACTIONS FOR YOU! THIS IS INTENTIONAL!

//

CHARACTER&

operator=( const CHARACTER& rhs ); // assignment operator

public

:

CHARACTER(

char* cstring);

~CHARACTER();

operator char*();

public

:

char* rep; // Actual string

size_t len;

// String length

};

CHARACTER::CHARACTER(

char* cstring) :

rep(cstring),

len(strlen(cstring))

{ }

CHARACTER::~CHARACTER()

{

}

CHARACTER::

operator char*()

{

return rep;

}

extern

"C" void __stdcall SUB(int& int1, int &, CHARACTER , CHARACTER , int &);

int

_tmain(int argc, _TCHAR* argv[])

{

// Calling convention test

printf(

"Calling FORTRAN... ");

int int1=1;

int int2=2;

int int3=3;

char* string1 = "I am the first string";

char* string2 = "I am the second string";

SUB(int1,int2,string1,string2,int3);

return 0;

}

subroutine sub(int1, int2, string1, string2, int3)

implicit none

integer int1, int2, int3

character*(*) string1, string2

C

write(6,*) int1,int2,int3

write(6,*) string1, string2

C

return

end

0 Kudos
Tony_Garratt
Beginner
2,602 Views

I should have added that we are compiling with /iface:cvf for this project.

Tony

0 Kudos
Steven_L_Intel1
Employee
2,602 Views
Thanks for the code - I won't get a chance to try this until Monday.

I will note that the use of "int" for the character lengths could be a problem - you should use size_t instead.
0 Kudos
Tony_Garratt
Beginner
2,602 Views

We are using t_size to hold the string lengths, which I believe should work on 64bit?

Thank you for trying out the code - I will be very interested to hear your conclusions.

Tony

0 Kudos
Steven_L_Intel1
Employee
2,602 Views
Tony,

As best as I can tell, the Fortran code is fine on x64. I built a Fortran equivalent of your C++ routine to pass the string addresses and lengths in the proper places, and it worked fine when built with /iface:cvf on x64.

Your C++ code, though, is doing something very bad on x64.. To show this, I first modified the Fortran code so that there were seven integer arguments and printed the locations of each.. I expect to see reasonable "address-looking" numbers for the integer and string address arguments, and to see 64-bit smallish integer values for the lengths. What I get however is this:

2358804 2358836 2358960
2358976 2358868 -3689348814741910324
7730941132

The first five look like addresses, but they shouldn't all be - the fourth value should be the length of string1.

I then looked to see what data was at these addresses (four bytes for the integers and 8 for the others) and got this:

00000001 00000002 0000000140117E38 0000000140117E50 CCCCCCCC00000003

int1 and int2 are ok. Darned if I know what the next two are - the first ought to be the data bytes of string1, but clearly is an address of some kind, as is the second one. The fifth entry appears to be partially uninitialized storage and might be just int3 (with unallocated bytes displayed.)

I'm not a C++ expert so I'll have to leave it to you to figure out what it is doing wrong.
0 Kudos
Tony_Garratt
Beginner
2,602 Views

Thanks so much for your reply - Ill take a look at the C++. Its good to know that the compiler supports CVF calling convention for 64bit - its just a case now of figuring out whats wrong with our C++.

thank you again,

Tony

0 Kudos
Tony_Garratt
Beginner
2,602 Views

Hi Steve,

Thanks for testing CVF works for Fortran calling Fortran on 64bit, but isnt the main use case of the calling convention that you want it for C++ calling Fortran? I see there is a sample that comes with the compiler, but that isnt using CVF, but it does give me the idea that perhaps our problem is that we arent using INTERFACE.

thanks

tony

0 Kudos
Steven_L_Intel1
Employee
2,602 Views
The main use of CVF convention is indeed mixed-language applications, whether it be something else calling Fortran or Fortran calling something else, where the "something else" was built to assume CVF conventions. Use of CVF is certainly not required for mixed-language work, and it is easier if you don't use the CVF option (as long as you make appropriate changes in the other code.)

The problem you're having is NOT on the Fortran side - that's working perfectly. It's the C++ code that is passing garbage in the x64 case. I don't lnow what MSVC does with __STDCALL on x64, but I think the problems are elsewhere, since arguments are being passed incorrectly (and __STDCALL affects stack cleanup after the call.)

INTERFACE won't help C++.
0 Kudos
Reply