How should multiple Fortran strings be passed to C?

回眸只為那壹抹淺笑 提交于 2019-12-12 12:49:10

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!