Fortran Calling C Routine - Hidden String Length Arguments

Fortran Calling C Routine - Hidden String Length Arguments

I'm calling C functions from Fortran and in general have this working.  I've run across some information on a feature where Fortran will append to the end of the argument list a size parameter for each CHARACTER variable in the argument list.  I've tried to get this to work based on discussions and samples in these forums but just can't get it to work.

Two questions:
1) I hope someone can review my code and point out what I've missed?
2) In what manual or documentation is this feature documented?

Fortran Program:

      program FortranCallingC
      USE,INTRINSIC :: ISO_C_BINDING

      implicit none
      
      interface
        subroutine CroutineX(c,i) bind(C, name="CroutineX")
          USE,INTRINSIC :: ISO_C_BINDING
          IMPLICIT NONE
          CHARACTER, DIMENSION(*), INTENT(INOUT) :: c
          INTEGER, INTENT(INOUT) :: i
        end subroutine CroutineX
      end interface 

      ! Variables
      character(LEN=40, KIND=C_CHAR) charString
      integer charStringLen/40/
      
      ! Body of FortranCallingC
      charString = "Stuff";
      call CroutineX(charString, charStringLen)
      
      print *, 'charString: ', charString
      
      stop

      end program FortranCallingC

C Routine:

// This is the main DLL file.

#include "stdafx.h"
#include <stdio.h>
#include <string>

#include "Croutine.h"

extern "C" __declspec(dllexport) void CroutineX(char* charString
                                              , int* charStringLen
                                              , size_t ivf_CharStringLen
                                               )
{
 printf("Croutine:\n\n");

 printf("%30s: %s\n", "charString", charString);
 printf("%30s: %12d\n", "*charStringLen", *charStringLen);
 printf("%30s: %12u\n", "ivf_CharStringLen", ivf_CharStringLen);

 printf("\n");

 return;

}

 

 

AttachmentSize
Downloadapplication/zip FortranCallingC.zip10.05 MB
13 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

For BIND(C) procedures there is no hidden argument.

Character dummy arguments interoperable with C (i.e. arguments in procedures that you can stick BIND(C) on) must have a length of one.  They can be arrays of any size, but they are arrays of single length character elements, not arrays of strings.  The interface block in your Fortran code is consistent with this.

This is by design - because the length must always be one there's no need to pass it, so there's no need to worry about hidden parameters and their type and their position in the argument list and all that kerfuffle for BIND(C) procedures (or C functions interoperable with such an interface).

Related, but as a separate concept, Fortran's sequence association rules for procedure arguments allow a default character kind or KIND=C_CHAR (if that's different from default character kind) character scalar string to be associated with the corresponding kind character array.  Consequently the compiler accepts you associating the charString scalar actual argument with the c dummy argument of the CroutineX procedure interface.

Related, but as another separate concept, when passing an array to an explicit shape or assumed size dummy argument (*) no size information is passed by the compiler (not in a way that the programmer can take advantage of, anyway).   Consequently, within a procedure with an explicit shape or assumed size dummy argument the array size information needs to be provided in some other manner by the programmer (assuming size information is needed in the case of an assumed size dummy array).  One option for doing this is to provide that size information as a separate argument - which is what you are already doing with your charStringLen actual argument (that corresponds to the i dummy).

So delete the ivf_CharStringLen C parameter and just use the charStringLen parameter to tell you haw many char objects there are in the array pointed to by charString.

Note strings in C that are represented by a char pointer are typically use a null terminator.  That null terminator won't be at the end of the Fortran string unless you explicitly arrange for it to be there (what is otherwise going to stop the %s printf format specifier from running off the end of your array?).  You could do this by changing your assignment statement for the character variable in Fortran to be something like charString = "stuff" // ACHAR(0).  Be careful that the null doesn't get chopped off the end of the Fortran string because of the way Fortran character assignment to a non-deferred length scalar works.  Once you know that the string is null terminated, there's no real need to pass any sort of length information (unless perhaps you are going to modify the length of the string proper on the C side).

I've no idea if or where IVF documents this (and I had a quick look), but the required behaviour is made clear in the Fortran standard. 

(You are using the /xx/ form of variable initialization on the fortran side, outside of a data statement.  Stop that - it will make you go blind.)

Excellent commentary by IanH.

The books referred to in Dr Fortran's blog - https://software.intel.com/en-us/blogs/2013/12/30/doctor-fortran-in-its-... - provide a good background on interoperability with C.  You can also refer to Fortran 2003 standard at http://www.j3-fortran.org/, especially section 15 and 15.4 to better understand this aspect.

I too am having difficulty finding the ifort documentation of the way character lengths are passed when ISO C binding is not invoked. It's a fairly common method; as OP mentioned, hidden default integer arguments are appended for the character arguments. Those are suppressed in normal use of ISO C binding.
Attempting to mix the methods of ISO C binding with the prior compiler dependent methods seems to involve some of the worse aspects of both.

Good points by Ian, FortranFan and Tim. Some further comments:

Ian is correct that if the procedure declaration has BIND(C), then any character arguments must have character length 1. Typically you'd declare such arguments as DIMENSION(*). However, the Fortran standard carves out an exemption when calling a procedure where the dummy argument is an explicit shape array of characters - you are allowed to pass a character variable or string of any length to such an argument. This bypasses the normal rule against passing a scalar to an array.

The information about how lengths are passed in the absence of BIND(C) is documented on this page in the Mixed Language Programming section of the manual.

Steve - Intel Developer Support

Quote:

Steve Lionel (Intel) wrote:

Good points by Ian, FortranFan and Tim. Some further comments:

The information about how lengths are passed in the absence of BIND(C) is documented on this page in the Mixed Language Programming section of the manual.


Your posted link gives permission denied.

Hmm - I think I see why. Let me get that fixed.

Steve - Intel Developer Support

Fixed - correct link is here (also edited in my post above).

Steve - Intel Developer Support

Steve,

Tim expresses interest in "Attempting to mix the methods of ISO C binding with the prior compiler dependent methods".  This may be very difficult as found out by Tim.

Also, in some cases such as when the C procedure is decorated with "__stdcall" [for interoperation with Fortran 95 and older code that was making use of some particular compiler-dependent approaches in 32-bit environments], it may not even be possible i.e., Fortran code with ISO C binding will not be able to interoperate with such C procedures in 32-bit ( "__stdcall" is not applicable in 64-bit, right?)

Can you please comment on this mixing aspect including on situations where "__stdcall" may be involved?

Thanks, 

In our implementation, you cannot use STDCALL with BIND(C). I know gfortran allows that and I have suggested to the developers that we relax the restriction, but it is still there. On x64, there is no STDCALL mechanism, though the STDCALL attribute still has its other effects (downcasing name and making pass-by-value the default.)

Steve - Intel Developer Support

Quote:

Steve Lionel (Intel) wrote:

In our implementation, you cannot use STDCALL with BIND(C). I know gfortran allows that and I have suggested to the developers that we relax the restriction, ....)

Yes Please! It would be so so nice to have interfaces with just 1 'non standard' feature rather than a whole list of them. Looking at some other implimenations eg gfortran there is a defacto standard of sorts for bind(C) with stdcall

Quote:

Steve Lionel (Intel) wrote:

Fixed - correct link is here (also edited in my post above).

 

Bummer, doesn't work again.

Thanks

That link was to the 14.0 documentation, which was removed. I suggest starting at https://software.intel.com/en-us/articles/intel-visual-fortran-composer-... and click on the Documentation link, which will always be current. You can then look things up from there.

I will comment that we do now allow ATTRIBUTES STDCALL with BIND(C).

I'm going to close this thread now - if you have new questions, please start a new thread.

Steve - Intel Developer Support

Leave a Comment

Please sign in to add a comment. Not a member? Join today