0

I have some fortran tests I would like to run in CTest using create_test_sourcelist. This is a utility that creates a driver in C or C++, which calls the fortran test routines and expects the signature:

int fortname(int argv, char** argc)

Can someone explain the correct way to interface this signature, probably with iso_c_bindings as I understand these are getting pretty standard. I believe it is the char** that is making me hurt -- there are plenty of examples around for the integer argument.

Thanks!

Eli S
  • 1,379
  • 4
  • 14
  • 35
  • Maybe this: http://stackoverflow.com/questions/9972743/creating-a-fortran-interface-to-a-c-function-that-returns-a-char gives you some ideas. Basically I guess, you need to work with c_ptr types. – haraldkl Jul 11 '12 at 07:23

2 Answers2

3

Assuming that the meaning of argc and argv in the context of a C program's main function apply, then you could do something like:

! Default binding label is already lower case, but for clarity 
! it is good practice to specify the binding label explicitly.
function fortname(argc, argv) bind(c, name='fortname')
  use, intrinsic :: iso_c_binding, only: c_int, c_ptr           
  implicit none          
  !----
  ! Note C std allows argc == 0
  integer(c_int), intent(in), value :: argc
  ! Lower bound set to match C convention where element 0 probably 
  ! is name of program.  May be zero size.
  type(c_ptr), intent(in) :: argv(0:argc-1)
  ! Function result.
  integer(c_int) :: fortname

To convert those arguments across to something that is easier to use in Fortran (and further assuming that your Fortran processor supports all applicable parts of the Fortran 2003 standard, noting that deferred length character components are not currently supported by at least one commonly used processor) you could then...

  !----
  ! Name of the program.
  character(:), allocatable :: prog_name      
  ! Type to use for arrays of pointers to variable length strings.
  type :: string
    character(:), allocatable :: item
  end type string
  ! Our arguments.  May be zero size.
  type(string) :: arguments(argc-1)      
  integer :: i   ! argument index.      
  !****
  if (argc > 0) then   ! argv has something useful
    ! make program name accessible to fortran code.        
    call c_f_string(argv(0), prog_name)
    ! make arguments accessible to fortran code.
    do i = 1, size(arguments)
      call c_f_string(argv(i), arguments(i)%item)
    end do
  else  ! no useful information provided in argv
    prog_name = ''
  end if

  ! Work with arguments%item and prog_name...      
  print "(A)", prog_name
  do i = 1, size(arguments) ; print "(A)", arguments(i)%item ; end do

  fortname = 0      
contains
  ! Copy a null terminated C string (specified via a non-null c_ptr) to an 
  ! allocatable deferred length default character variable.
  subroutine c_f_string(c_string, f_string)
    use, intrinsic :: iso_c_binding, only: c_char, c_null_char, c_f_pointer
    !----
    type(c_ptr), intent(in) :: c_string
    character(:), intent(out), allocatable :: f_string
    !----
    ! Array for accessing string pointed at by C pointer 
    character(kind=c_char), pointer :: string_ptr(:)
    integer :: i    ! string index
    interface
      ! Steal std C library function rather than writing our own.
      function strlen(s) bind(c, name='strlen')
        use, intrinsic :: iso_c_binding, only: c_ptr, c_size_t
        implicit none
        !----
        type(c_ptr), intent(in), value :: s
        integer(c_size_t) :: strlen
      end function strlen
    end interface
    !****
    ! Map C pointer to fortran character array
    call c_f_pointer(c_string, string_ptr, [strlen(c_string)])
    ! Allocate fortran character variable to the c string's length
    allocate(character(size(string_ptr)) :: f_string)
    ! Copy across (with possible kind conversion) characters
    forall (i = 1:size(string_ptr)) f_string(i:i) = string_ptr(i)
  end subroutine c_f_string      
end function fortname
IanH
  • 21,026
  • 2
  • 37
  • 59
  • Thanks, just what I was looking for. This is a great example of how to make a Fortran main program wrapped in a C int main(). Now hopefully I understand CMake's create_test_sourcelist semantics correctly. – zbeekman May 01 '13 at 18:01
0

Your fortran declaration would be something like this (I think you got argc and argv mixed up so I swapped them):

function fortname(argc, argv) bind(c)
    use iso_c_binding
    integer(c_int), value :: argc
    type(c_ptr) :: argv
end function

You then need to write some code to convert argc to a fortran type. You can convert c pointer to fortran pointers using the c_f_ptr instrinsic. The tricky bit is that you have the double indirection, since fortran doesn't support the concept of a pointer to a pointer. Possibly the following would work, which prints out each argument

type(c_ptr), pointer :: argv_f
character(c_char), pointer :: string_n

call c_f_ptr(argv, argv_f)
do n=1,argc
    call_c_f_ptr(argvf(n), string_n)
    print *, string_n
end do

Disclaimer: I haven't compiled or run this code!

DaveP
  • 6,952
  • 1
  • 24
  • 37