1

I need to bind a C function in Fortran. The C function's prototype is something like

/* If argv[i] is NULL, then i is the length of argv[] */
int foo(char* argv[]);

I want to call foo in a Fortran subroutine bar

! If argv(i) is a blank string, then i is the length of argv.
! Constant MAX_STRING_LEN is the maximal length of any possible element in argv.
subroutine bar(argv) 
  character(len=*), intent(in) :: argv(*)

  character(kind=c_char), dimension(*), allocatable :: argv_c(*) !???

  ! convert argv to argv_c element by element, something like
  ! allocate(argv_c(len_trim(argv(i)) + 1) ! Plus 1 to put C_NULL_CHAR
  ! assign argv(i) to argv_c(i)

  ierror = foo(argv_c)
  ! deallocate each argv_c(i)
end subroutine bar

I want to know how to declare a prototype for foo. I designed this one, but I am not sure if it is correct. Is this argv interoperable with char**?

function foo(argv) bind(c, name="foo")
    character(kind=c_char), dimension(*), intent(in) :: argv(*)
end function

3 Answers3

2

Similar question and answer: Creating a FORTRAN interface to a C function that returns a char* Little bit difficult to orient in there (at least for me).

You must pass pointer to the array, because of the char**

function foo(argv) bind(c, name="foo")
    type(c_ptr) :: argv  !note no `value` here
                         ! what is the `intent` to put here?
end function

The normal array argument would pass just a char* which you do not want.

First try:

ierror = foo(c_loc(argv_c))

The above would work if the C part does not allocate it, which it probably does. The problem is then to get the length of the string.

type(c_ptr) :: argv_c_ptr
character(kind=c_char),pointer :: argv_c(:)

ierror = foo(argv_c_ptr)

Now you must find out length of the array, I would call C procedure strlen (see C_strlen in http://fortranwiki.org/fortran/show/c_interface_module). After that you can call:

call c_f_pointer(argv_c_ptr, argv_c, [len])

Do not forget to deallocate the C allocated string properly, do not use Fortran `deallocate for that.

Community
  • 1
  • 1
  • I see. argv_c in Fortran must be an array of c_ptr. And for each arv_c(i), I need to do dynamic memory allocation to copy argv(i) to argv_c(i) and put the NULL character. That is horrible. – Junchao Zhang Mar 24 '14 at 22:27
  • 1
    No `argv_c` is not an array of `c_ptr's`, it is an `c_ptr` pointer to an array. But again, it is not clear form your code, what does the c-procedure actually does? I supposed it allocates the string. Also note I just corrected an error in my code. – Vladimir F Героям слава Mar 24 '14 at 22:34
  • I mean, if it just accepts the string from Fortran, you can just do the `foo(c_loc(argv_c))`. – Vladimir F Героям слава Mar 24 '14 at 22:37
  • the C function just reads the input argv, and does other things. In other words, I want Fortran to allocate temp memory and prepare argv_c in a good shape before calling C. – Junchao Zhang Mar 24 '14 at 22:52
  • 1
    In that case use my prototype, call using c_loc(argv_c) and use the code in your question for allocating, it is quite simple. But the char** for just reading is strange. – Vladimir F Героям слава Mar 24 '14 at 23:10
2

The C declaration char *argv[] is array of pointer to char. The equivalent Fortran argument declaration is TYPE(C_PTR) :: argv(*), where each element in the array is the C address of a character of C kind.

The usual C convention is that the character is actually the first character in an array of character (i.e. you have the C address of an array of character of C kind), and the length of that array of char is null terminated (i.e. all up you have an array of pointers to C strings). The comment in your C code also indicates that the size of the array of pointers is indicated by a final array element of NULL. This is consistent with the conventions around argv arguments in C.

To be able to find the C address of an object in Fortran, the object must have the TARGET attribute. The results of evaluating an expression does not have the target attribute.

As written, the bar subroutine has no idea about the size of the argv dummy array (it is an assumed size argument). However, if the argv dummy argument was perhaps assumed shape, you could do something like:

SUBROUTINE bar(argv)
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_LOC, C_CHAR, C_INT,  &
      C_NULL_CHAR, C_NULL_PTR
  ! The input to bar (?) that needs to be converted for use in the C function.
  CHARACTER(*), INTENT(IN) :: argv(:)
  ! The array of C pointers.
  TYPE(C_PTR) :: argv_c(SIZE(argv) + 1)
  ! A local type to simplify memory management.
  TYPE string
    CHARACTER(LEN=:,KIND=C_CHAR), ALLOCATABLE :: item
  END TYPE string
  ! Temporary arrays of character.  Need to have TARGET for C_LOC.
  TYPE(string), TARGET :: tmp(SIZE(argv))
  ! Utility index.
  INTEGER :: i                    

  ! The interface for the C function
  INTERFACE
    FUNCTION foo(argv) BIND(C, NAME='foo')
      USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_INT
      IMPLICIT NONE
      ! INTENT(IN) here assuming foo doesn't modify the array of pointers.
      TYPE(C_PTR), INTENT(IN) :: argv(*)
      INTEGER(C_INT) :: foo
    END FUNCTION foo
  END INTERFACE

  INTEGER(C_INT) :: ierror

  DO i = 1, SIZE(argv)
    ! This may involve kind conversion.
    tmp(i)%item = TRIM(argv(i)) // C_NULL_CHAR
    argv_c(i) = C_LOC(tmp(i)%item)
  END DO
  ! Size of the argv array on the C side indicated by a NULL.
  argv_c(SIZE(argv_c)) = C_NULL_PTR

  ierror = foo(argv_c)
  ...

  ! memory allocated for the components of the tmp object (and hence 
  ! the things pointed at by the argv_c array) will go away when this 
  ! subroutine terminates.
END SUBROUTINE bar
IanH
  • 21,026
  • 2
  • 37
  • 59
  • Thanks to @IanH. Your explanation is amazing. I think Fortran textbooks should add this char** case, instead of just talking about char*. Since char** is common, and it is very different from passing char*. BTW, in my case, argv is assume-sized, because the interface designer thought the callee can decode argv's length by looking for a blank line. – Junchao Zhang Mar 25 '14 at 15:45
  • Note that what's appropriate for `char **` depends on the nature of the thing being pointed to. The comments in the code in your question (and the name of the argument) suggest the approach in this answer - it may not always be appropriate. – IanH Mar 25 '14 at 21:22
0

The standard does not provide an easy way to write a Fortran interoperable routine that accepts a string from C. You must declare the dummy argument as an array of single characters. You can use the combination of the C_LOC and C_F_POINTER procedures, both defined in intrinsic module ISO_C_BINDING, to "cast" a character array argument to a Fortran pointer to a character string. An example follows:

subroutine Interop_sub(arg) BIND(C)
USE, INTRINSIC :: ISO_C_BINDING
CHARACTER, DIMENSION(*) :: ARG
CHARACTER(1000), POINTER :: LCL_ARG ! Local pointer to argument 
CALL C_F_POINTER(C_LOC(ARG), LCL_ARG) 

From HERE(http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-ECF6FF36-1397-43D8-88BB-CCD2F6108DA5.htm)

HERE is an additional link for BIND(C)

There are also some translation concepts (C to FORTRAN and visa versa) HERE if you can use them

ryyker
  • 22,849
  • 3
  • 43
  • 87