Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28990 Discussions

how to pass argv[] from C to Fortran ?

coolweather
Beginner
2,203 Views
Tell me please,how to pass argv[] from C to Fortran ?
in C
[bash]int main(int argc, char * argv[]) {...}[/bash]
char *argv is a command line string parameter
when pass it to fortran I do it like this
[bash]extern void FortranRoutine(char * fname, int nlen, ...);

int n = strlen(argv);
FortranRoutine(argv, &n, ...);[/bash]

in Fortran code
[bash]subroutine FortranRoutine(fname, nlen,...) bind(c, name ='FortranRoutine')
use iso_c_binding, only: c_int, c_float, c_char
character(kind=c_char), dimension(*), intent(in) :: fname integer(c_int), intent(in) :: nlen ! here I have a string with fixed length character(len=50) :: param ! and I need to transform my 'fname' (or argv) to fixed length string 'param' ... end subroutine[/bash]

Could you explain me how to do it, please ?
because I have the mismatch types error (between param and fname)
Thank you !
0 Kudos
10 Replies
Arjen_Markus
Honored Contributor I
2,203 Views
If the only reason to use a C main program is that you have access to the command-line
arguments, then you can use the standard routines that come with Fortran 2003 instead:

get_command, get_command_argument and command_argument_count.

Otherwise things are a trifle tricky, as argv is an array of pointers to strings, something
Fortran does not support directly. You will have to treat the array elements as individual
pointers. Not very helpful, I know, but I do not jave time to explain it in more detail at
the moment.

Regards,

Arjen
0 Kudos
anthonyrichards
New Contributor III
2,203 Views
Why don'tyou start by sending the address of (or pointer to) argv and receive it on the Fortran side as an integer value. Then use debug to take the received address and look at the memory location pointed to by the received address? You can then have a look in the memory window at what is stored from that point and see if you can make sense of the character strings (making use of the length parameter as a guide)? Then you can start reading the memory and copying the strings that you want.
0 Kudos
coolweather
Beginner
2,203 Views
main C is used not only to pass argv to Fortran, but also for many other computings.
anthonyrichardscould you explain me please how to code your ideas ?
is it the only way to recieve a string(char *) from C ?
0 Kudos
mecej4
Honored Contributor III
2,203 Views
C strings of arbitrary length are not fully supported by the ISO_C_BINDING module. The following works (on Linux-32 and Win-32; not tested elsewhere), but may not work with other compilers or OSes.

The C main program:

[cpp]#include 
#include 
int main(int argc, char *argv[]){
int i;
for(i=0; i,strlen(argv));
}
[/cpp]
The Fortran subroutine:

[fortran]    subroutine FtnRoutine(fname, nlen) bind(c, name ='FtnRoutine') 
      
        use iso_c_binding, only: c_int, c_float, c_char  
      
        integer(c_int), intent(in), value         ::  nlen  
        character(kind=c_char), intent(in) ::  fname(nlen)
      
        ! here I have a string with fixed length  
        character(len=50) :: param  
      
        do i=1,nlen
           param(i:i)=fname(i)
        end do
param(nlen+1:50)=''
write(*,*)' param = ',param return end subroutine [/fortran]
Compile and run:

$ gcc -m32 -c caller.c
$ ifort -nofor-main caller.o ssub.f90
$ ./a.out 11 22 33

param = ./a.out
param = 11
param = 22
param = 33

0 Kudos
IanH
Honored Contributor III
2,203 Views
If you want to send a single string (argv) then option one - copy from the array to a scalar (assumes here that kind c_char is iso 10646, ascii or default so the assignment works, which is a pretty safe assumption)

[fortran]subroutine FortranRoutine(fname, nlen) bind(c, name='FortranRoutine')
  use, intrinsic :: iso_c_binding, only: c_int, c_char
  implicit none
 character(kind=c_char), intent(in) :: fname(*)
  integer(c_int), intent(in), value :: nlen
  !----
  character(len=nlen) :: param
  integer :: i
  !****
  forall (i=1:nlen) param(i:i) = fname(i)   ! Or do i = 1, nlen; ...
  print *, param
end subroutine FortranRoutine[/fortran]

Option two - map C pointer to Fortran pointer.

[fortran]subroutine FortranRoutine(fname, nlen) bind(c, name='FortranRoutine')
  use, intrinsic :: iso_c_binding, only: c_ptr, c_int, c_char, c_f_pointer
  implicit none
  type(c_ptr), intent(in), value :: fname
  integer(c_int), intent(in), value :: nlen
  !----
  ! Std quibble - does nlen have to be a /constant-expression/ for this to be conforming?
  ! I'm not sure...
  character(len=nlen,kind=c_char), pointer :: param
  !****
  call c_f_pointer(fname, param)
  print *, param
end subroutine FortranRoutine[/fortran]

If you want to send the entire array of strings (i.e. pass argv, which is char ** ):

[fortran]module my_mod
  implicit none
contains
  ! Because this takes at least one argument by value, an explicit interface is required.
  subroutine FortranRoutine(...)
    ! option one or two as above ...
  end subroutine FortranRoutine
 
  subroutine FortranRoutineDeluxe(argc, argv) bind(c, name='FortranRoutineDeluxe')
    use, intrinsic :: iso_c_binding: c_ptr, c_int
    interface
      function strlen(s) bind(c, name='strlen') result(l)
        use, intrinsic :: iso_c_binding, only: c_ptr, c_size_t
        implicit none
        type(c_ptr), intent(in), value :: s
        integer(c_size_t) :: l
      end function strlen
    end interface
    integer(c_int), intent(in), value :: argc
    type(c_ptr), intent(in) :: argv(argc)
    !---
    integer :: i
    integer(c_int) :: l
    !****
    ! Note argv(1) is the program name.
    do i = 1, ubound(argv,1)
      l = strlen(argv(i))
      call FortranRoutine(argv(i), l)
    end do
  end subroutine FortranRoutineDeluxe
end module my_mod[/fortran]
The C prototype for the full argv option is:

void FortranRoutineDeluxe(int, char **);
0 Kudos
coolweather
Beginner
2,203 Views
thank you so much for this detailed explanation!
I would like to precise:
- why it is necessary to use the option INTRINSIC in
use, intrinsic :: iso_c_binding ?
- if I pass 'nlen' without VALUE option, the declaration of interface is not necessary ?
isthe value option used only for confort of passing variables or there is another reason ?
0 Kudos
anthonyrichards
New Contributor III
2,203 Views
There are TWO modules: one is called iso_C_binding.modintr, which is the one you need and which you get when you use the INTRINSIC qualifier. Otherwise you can get iso_C_binding.mod which is obtained by compiling from the iso_c_binding.f90 file in th compiler's INCLUDE folder..
0 Kudos
mecej4
Honored Contributor III
2,203 Views
- why it is necessary to use the option INTRINSIC in
use, intrinsic :: iso_c_binding ?

To prevent the problems that might occur if you had your own module with the same name, but did not remember that it existed. The INTRINSIC forces the vendor-provided module to be used.
- if I pass 'nlen' without VALUE option, the declaration of interface is not necessary ?
isthe value option used only for confort of passing variables or there is another reason ?

Whatever changes you make to the Fortran part of your sources has no effect on the C compiler. Therefore, you have to know what parameters the Fortran compiler expects (which can be affected by interfaces), whether by value or reference, and make sure that the C caller delivers the expected parameters.

To pass the string length by reference, instead of by value, you would need to create a nonce variable, assign the string length to it, and pass its address -- awkword, and of no benefit here.
0 Kudos
Triple_Thunder
Beginner
2,203 Views
Previous External Development Guide: Using CALL_EXTERNAL Next

Fortran Examples

Example: Calling a Fortran Routine Using a C Interface Routine

Calling Fortran is similar to calling C, with the significant difference that Fortran code expects all arguments to be passed by reference and not by value (the C default). This means that the address of the argument is passed rather than the argument itself. This issue is discussed in By-Value and By-Reference Arguments.

A C interface routine can easily extract the addresses of the arguments from the argv array and pass them to the actual routine which will compute the sum. The arguments f, n, and s are pointers that are being passed by value. Fortran expects all arguments to be passed by reference - that is, it expects all arguments to be addresses. If C passes a pointer (an address) by value, Fortran will interpret it correctly as the address of an argument. The following code segments illustrate this. The example_c2f.c file contains the C interface routine, which would be compiled as illustrated above. The example.f file contains the Fortran routine that actually sums the array.

In these examples, we assume that the routines are being compiled under Sun Solaris. The object name of the Fortran subroutine will be sum_array1_ to match the output of the Solaris Fortran compiler. The following are the contents of example_c2f.c and example.f:

Table 3-6: C Wrapper Used to Call Fortran Code (example_c2f.c)
Table 3-6: C Wrapper Used to Call Fortran Code (example_c2f.c)
C
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
#include   
  
void sum_array(int argc, void *argv[])  
{  
  extern void sum_array1_();	/* Fortran routine */  
  int *n;  
  float *s, *f;  
   
  f = (float *) argv[0];    /* Array pntr */  
  n = (int *) argv[1];      /* Get # of elements */  
  s = (float *) argv[2];    /* Pass back result a parameter */  
   
  sum_array1_(f, n, s);     /* Compute sum */  
}  

Table 3-7: Fortran Code Called from IDL via C Wrapper (example.f)
Table 3-7: Fortran Code Called from IDL via C Wrapper (example.f)
f77
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
c This subroutine is called by SUM_ARRAY and has no IDL-specific code.  
c  
SUBROUTINE sumarray1(array, n, sum)  
INTEGER*4 n  
REAL*4 array(n), sum  
  
sum=0.0  
DO i=1,n  
sum = sum + array(i)  
PRINT *, sum, array(i)  
ENDDO  
  
RETURN  
END  

This example is compiled and linked in a manner similar to that used in the C example above. For more information on compiling and linking on your platform, see the README file contained in the external/call_external/Fortran subdirectory of the IDL distribution. This directory also contains a makefile, which builds this example on UNIX platforms. To call the example program from within IDL:

;Make an array.  
X = FINDGEN(10)  
;A floating result  
SUM = 0.0  
S = CALL_EXTERNAL('example.so', $  
'sum_array', X, N_ELEMENTS(X), sum)  

In this example, example.so is the name of the sharable image file, sum_array is the name of the entry point, and X and N_ELEMENTS(X) are passed to the called routine as parameters. The returned value is contained in the variable sum.

Hidden Arguments

When passing C null-terminated character strings into a Fortran routine, the C function should also pass in the string length. This extra parameter is added to the end of the Fortran routine call in the C function, but does not explicitly appear in the Fortran routine.

For example, in C:

char * str1= 'IDL';  
char * str2= 'ITT';  
int len1=3;  
int len2=3;  
double data, info;  
/* Call a Fortran sub-routine named example1 */  
example1_(str1, data, str2, info, len1, len2)  

In Fortran:

SUBROUTINE EXAMPLE1(STR1, DATA, STR2, INFO)  
CHARACTER*(*)STR1, STR2  
DOUBLE PRECISIONDATA, INFO  

Example: Calling a Fortran Routine Using a Fortran
Interface Routine

Calling Fortran is similar to calling C, with the significant difference that Fortran expects all arguments to be passed by reference. This means that the address of the argument is passed rather than the argument itself. See By-Value and By-Reference Arguments for more on this subject.

A Fortran interface routine can be written to extract the addresses of the arguments from the argv array and pass them to the actual routine which will compute the sum. Passing the contents of each argv element by value has the same effect as converting the parameter to a normal Fortran parameter.

This method uses the OpenVMS Extensions to Fortran, %LOC and %VAL. On IBM AIX, the LOC function is an intrinsic operator. The syntax of the call, which differs from that used on other platforms, is:

y=loc(x)  

Some Fortran compilers may not support these extensions. If your compiler does not, use the method discussed in the previous section for calling Fortran with a C interface routine.

The contents of the file example1.f are shown in the following figure. This example is compiled, linked, and called in a manner similar to that used in the C example above. For more information on compiling and linking on your platform, see the README file contained in the external/fortran subdirectory of the IDL distribution. This directory also contains a makefile, which builds this example on UNIX platforms.


Note
This example is written to run under a 32-bit operating system. To run the example under a 64-bit operating system would require modifications; most notably, to declare argv as INTEGER*8 rather than INTEGER*4.

Table 3-8: Fortran Code Called Directly From IDL
Table 3-8: Fortran Code Called Directly From IDL
f77
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
SUBROUTINE SUM_ARRAY(argc, argv)!Called by IDL  
INTEGER*4 argc, argv(*)!Argc and Argv are integers  
  
j = LOC(argc)!Obtains the number of arguments (argc)  
!Because argc is passed by VALUE.  
  
c Call subroutine SUM_ARRAY1, converting the IDL parameters  
c to standard Fortran, passed by reference arguments:  
  
CALL SUM_ARRAY1(%VAL(argv(1)), %VAL(argv(2)), %VAL(argv(3)))  
RETURN  
END  
  
c This subroutine is called by SUM_ARRAY and has no  
c IDL specific code.  
c  
SUBROUTINE SUM_ARRAY1(array, n, sum)  
INTEGER*4 n  
REAL*4 array(n), sum  
  
sum=0.0  
DO i=1,n  
sum = sum + array(i)  
ENDDO  
RETURN  
END  

To call the example program from within IDL:

X = FINDGEN(10) ; Make an array.  
sum = 0.0  
S = CALL_EXTERNAL('example1.so', $  
'sum_array_', X, N_ELEMENTS(X), sum)  

In this example, example1.so is the name of the sharable image file, sum_array_ is the name of the entry point, and X and N_ELEMENTS(X) are passed to the called routine as parameters. The returned value is contained in the variable sum.


Note
The entry point name generated by the Fortran compiler may be different than that produced by the C compiler. One of the best ways to find out what name was generated is to use the UNIX nm utility on the object file. See your system's man page for nm for details.

IDL Online Help (March 06, 2007)

0 Kudos
Steven_L_Intel1
Employee
2,203 Views
Please use the Fortran standard intrinsics for querying the command line. That's what they're there for.
0 Kudos
Reply