4

I want to call a C function from Fortran and receive a string of characters defined in the C function. I searched, but I couldn't find a working, straightforward answer, so far.

Actually I have found a workaround: receiving an array of characters instead, then using the intrinsic function transfer to put the result into a Fortran string. Here is my working code.

Fortran main program:

program pr
    implicit none
    character(200) :: stringa

    call strfromc(stringa)
    write (6,*) 'FPR stringa: "', trim(stringa),'"'

    stop
end program pr

Fortran subroutine:

subroutine strfromc(stringa)
    use iso_c_binding
    implicit none

    character(200) :: stringa

    interface
        subroutine getstrfromc(ld,d) bind(c,name='getString')
            import :: c_int, c_ptr
            integer(c_int) :: ld
            type(c_ptr), value :: d
        end subroutine getstrfromc
    end interface

    ! declare a character array of type c_char and sufficient length:
    character(c_char), dimension(200), target :: d1

    integer :: ld1 ! will contain the string length
    integer :: i

    ! the C pointer of character array d1 is passed:
    call getstrfromc(ld1, c_loc(d1))

    ! read the first ld1 elements of array d1 as a character string, which will be returned:
    stringa= transfer(d1(1:ld1), stringa)

    write (6,*) 'SF d1: ', (d1(i),i=1,ld1)
    write (6,*) 'SF stringa: "', trim(stringa),'"'
    write (6,*) ''

    return
end subroutine

C function:

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

void getString(int * lw, char * w) {
    printf("Enter a string:\n");
    scanf("%[^\n]", w); //scanning the whole string, including the white spaces

    *lw= strlen(w);

    printf("C: w: %s\n", w);
    printf("C: lw: %d\n", *lw);
    printf("\n");

    return;
}

Can anyone suggest a more direct way?

  • 1
    Do you know that a `character(len=200) a` actual actual argument can be argument associated with a `character(len=1) b(*)` (or `b(200)`) dummy argument? – francescalus Dec 09 '19 at 12:54
  • @francescalus I need d1 to be a "target". I tried using it as dummy argument, the compiler said I then need an explicit interface for subroutine strfromc – Francesco Uboldi Dec 09 '19 at 16:05
  • Yes, if you have `strfromc` with a target dummy argument you'll need it that subroutine to have an explicit interface the main program. If you give it one (putting `strfromc` in a module which is used, say), then does it work? – francescalus Dec 09 '19 at 16:33
  • YES, it does, thank you! I am posting my new code in an "answer". – Francesco Uboldi Dec 10 '19 at 09:54

2 Answers2

2

Why not simply use C_CHAR kind? I "compressed" your code a bit:

program main
  use, intrinsic :: iso_c_binding, only: C_INT, C_CHAR
  implicit none
  integer         :: lw ! will contain the string length
  character(256)  :: w = "" ! will contain the string; Initialize to empty

  ! C interface
  interface
    subroutine getstrfromc( lw, w ) bind( c, name='getString' )
      import :: C_INT, C_CHAR
      implicit none
      integer(C_INT), intent(out)     :: lw
      character(C_CHAR), intent(out)  :: w(*)
    end subroutine getstrfromc
  end interface

  ! In Fortran variables are passed by default as "pointers".
  call getstrfromc( lw, w )

  ! Write string
  write (*,*) "Fortran: w: ", trim(w)
  write (*,*) "Fortran: lw:", lw

end program main

The C function remains the same.

jcerar
  • 467
  • 4
  • 13
0

I post this in an answer because it appears cleaner. Here is my modified code following @francescalus' suggestion.

Fortran main program:

program pr
    use sfc
    implicit none
    character(200) :: stringa

    call strfromc(stringa)
    write (6,*) 'FPR stringa: "', trim(stringa),'"'

    stop
end program pr

Fortran module including the interface to the C function and the Fortran subroutine:

module sfc
    use iso_c_binding
    implicit none

    interface
        subroutine getstrfromc(ld,d) bind(c,name='getString')
            import :: c_int, c_ptr
            integer(c_int) :: ld
            type(c_ptr), value :: d
        end subroutine getstrfromc
    end interface

contains

subroutine strfromc(d1)
    use iso_c_binding
    implicit none

    ! declare a character array of type c_char and sufficient length:
    character(c_char), dimension(200), target :: d1

    integer :: ld1 ! will contain the string length
    integer :: i


    ! the C pointer of character array d1 is passed:
    call getstrfromc(ld1, c_loc(d1))

    ! clean elements after the ld1-th:
    d1(ld1+1:)= ''

    write (6,*) 'SF d1: ', (d1(i),i=1,ld1)
    write (6,*) ''

    return
end subroutine

end module sfc

The C function remains the same.

... other suggestions?!?