问题
To pass a Fortran string to C, a hidden parameter is also passed with the variable's size. Here's a working fortran definition, and the C (actually C++/CLI) method:
interface
subroutine AppendExtension(
+ Filename)
+ bind(C, name="AppendExtension")
character *1, intent(inout):: Filename
end subroutine AppendExtension
end interface
and here's the C++/CLI that gets called:
extern "C" {
void __declspec(dllexport) __cdecl AppendExtension(
char * name,
int buffersize)
{
String^ clistr = gcnew String(name);
clistr = System::IO::Path::ChangeExtension(clistr->Trim(), gcnew String("OUT"));
IntPtr p = Marshal::StringToHGlobalAnsi(clistr);
char *pNewCharStr = static_cast<char*>(p.ToPointer());
int cstrlen = strlen(pNewCharStr);
memcpy_s(name, buffersize, pNewCharStr, cstrlen);
if (cstrlen < buffersize)
{
// backfill with spaces, since a Fortran string is spaces on the right.
memset(&name[cstrlen], ' ', buffersize-cstrlen);
}
Marshal::FreeHGlobal(p);
}
The above is working properly (Intel Visual Fortran 2013 SP1).
I now want to pass two strings into a function. Here's what I did:
interface
subroutine ParseSpec(
+ Filename, Corename)
+ bind(C, name="ParseSpec")
character *1, intent(inout):: Filename
character *1, intent(inout):: Corename
end subroutine ParseSpec
end interface
Here's the call in action:
CHARACTER*80 FILE_SPEC
CHARACTER*200 CORE_NAME
CALL ParseSpec(FILE_SPEC, CORE_NAME)
and here's the C++/CLI:
void __declspec(dllexport) __cdecl ParseSpec(char * name, char * corename, int namelen, int corelen)
{
// namelen and corelen both contain the length of "name".
...
There are two hidden variables. I think they should be for the buffersizes for each of my two strings, one for "Filename" and one for "Corename". But both contain the buffer size of the first buffer, namely 80.
Where am I going wrong?
回答1:
The calling convention for BIND(C) procedures can be quite different from the default calling convention that a Fortran compiler uses for Fortran to Fortran calls. You should not rely on any hidden CHARACTER length arguments (in the general case, you also shouldn't expect that the hidden arguments are of type int
). I suspect you have just been lucky, given the way that the Fortran CHARACTER variable is laid out in memory by that compiler.
There is no need for the compiler to pass the length for a BIND(C) procedure, because the only length of a CHARACTER variable interoperable with C is one. However, if the dummy argument is an array (which is what you want if you are passing a string, rather than an individual character), the SIZE of that array may be non-negative - the rules of Fortran sequence association for KIND=C_CHAR character mean you can associate a scalar with an array.
All up - change the Fortran function declaration such that the character arguments are arrays and add arguments to explicitly pass the length of those arrays.
Using free form source, and assuming that default character is the same as C_CHAR kind:
interface
subroutine ParseSpec( &
Name, Name_len, &
Corename, Corename_len ) &
bind(C, name="ParseSpec")
use, intrinsic :: iso_c_binding, only: c_int, c_char
integer(c_int), value :: Name_len
character(kind=c_char), intent(inout) :: Name(Name_len)
integer(c_int), value :: Corename_len
character(kind=c_char), intent(inout):: Corename(Corename_len)
end subroutine ParseSpec
end interface
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT
CHARACTER*80 FILE_SPEC
CHARACTER*200 CORE_NAME
CALL ParseSpec( &
FILE_SPEC, LEN(FILE_SPEC, KIND=C_INT), &
CORE_NAME, LEN(CORE_NAME, KIND=C_INT) )
extern "C" void ParseSpec(
char* name, int name_len,
char* corename, int corelen );
// Operations on name[0] to name[name_len-1] and
// corename[0] through corename[core_len-1]...
来源:https://stackoverflow.com/questions/34822683/how-should-multiple-fortran-strings-be-passed-to-c