1-11  FORTRAN/C INTEROPERABILITY 
 ********************************
 (Thanks to Craig Burley for the comments)

(partially based on the Fortran FAQ)

 FORTRAN is well suited to numerical computations, C is suitable for 
 system programming tasks.  A combination of the two languages in one 
 program should have been the best solution for some programs.

 It seems simple enough, we can easily create object modules from both 
 FORTRAN and C code, the object modules will be surely compatible if 
 made with the native compilers, then we can link edit them together. 
 Unfortunately some issues make this seemingly simple interfacing 
 highly platform dependent. 

 Note that for some systems, you have to initialize a runtime system 
 (shared libraries) explicitly if you call a different language, 
 and stopping execution from the other program may not work properly.

 
 Cfortran package
 ----------------
 One solution is to use the amazing 'cfortran' package, an include 
 file that performs the interfacing using the C language preprocessor.
 It takes some time to study the documentation, and it may overtax 
 the preprocessor.

 The cfortran package makes Fortran/C interfacing quick and easy 
 for a wide range of platforms.  The most recent version of the 
 include file "cfortran.h", and the documentation is available 
 via anonymous ftp from:

    ftp://zebra.desy.de/cfortran

 and via www at http://www-zeus.desy.de/~burow .


 Manual interfacing 
 ------------------
 If you prefer to do it manually, look first in your COMPILER 
 DOCUMENTATION, Sun and IRIX have a lot to say on this subject.

 A multi-platform (with some minor changes) example can be found in
 the chapter on "variable size arrays".

 There are some general issues you must be aware of before starting
 such work:

 
 Routines names
 --------------
 On some machines (e.g. UNIX) the FORTRAN compiler appends a trailing 
 underscore to FORTRAN routine names, both in subroutine/function 
 definitions and when calling them. 

 Why add the underscore suffix? Possible explanations are:

   o  Prevent name clashes of user-written routines with the 
      routines in the system libraries used by the compiler 
      at link time (the routine names in these libraries 
      usually don't have a trailing underscore). 

      For example, UNIX system routines may have simple names,
      that may be used in a user program.

   o  Prevent "amateurish" mixed-language programming. 
      Without proper understanding these attempts may produce 
      erroneous results. 

 In mixed-language programs on such machines, the linker will have a 
 problem when trying to match routine calls and routine code in the 
 object code. 

 Either the FORTRAN CALL statement (with underscores appended) will
 reference a non-FORTRAN routine, or the other language may call a 
 FORTRAN routine (also with underscores appended).  In each case 
 there will be a mismatch, which can be avoided by manually
 supplying the required underscores:

   o  If a FORTRAN routine calls a routine written another 
      language, append a trailing underscore IN THE DEFINITION 
      (in the code) of the non-FORTRAN routine.

   o  If a non-FORTRAN routine calls a FORTRAN routine, 
      append an underscore in THE CALLING STATEMENT to 
      the FORTRAN routine name.

 Compilers which behave like that may transform external user-defined 
 names in the following way:

    Appends a trailing underscore      Name remains unaffected
    -----------------------------      -----------------------
    Main program name                  Blank COMMON name '_BLNK__'

    Named COMMON blocks                Intrinsic functions names

    BLOCKDATA subprograms

    All names implicitly or 
    explicitly declared EXTERNAL


 Check your compiler documentation, you may not have to add anything,
 if your compiler/linker does it another way. 

 For example, VMS differentiates between user and system defined names 
 by having the later contain a '$' sign, users are advised not to use 
 that sign in names.

 There may be a similar problem if the compiler/linker changes the case 
 of routine names (e.g. CRAY uppercasing).


        CALLING C ROUTINES FROM FORTRAN
        ===============================

   Machine        C Routine name
   -------        ------------------
   ALPHA/DUNIX    lowercase letters_  (default)
   ALPHA/VMS      anything
   CRAY           UPPERCASE LETTERS
   HP             lowercase letters   (?)
   IBM/AIX        lowercase letters   (all IBM's ?)
   SGI/IRIX       lowercase letters_
   SUN            lowercase letters_
   TITAN          UPPERCASE LETTERS
   VAX/VMS        anything
   VAX/ULTRIX     lowercase letters_  (default)


 Variable passing mechanisms
 ---------------------------
 Fortran usually passes its variables by reference (passes pointers). 
 That means that you MUST give addresses in your calling C-program.

 Function results, may be passed by value, for example, the following 
 code calls a FORTRAN function called "gamma":

   double   x, y;
   ..................
   x = 2.0; 
   y = gamma_(&x) 



 Array storage order
 -------------------
 Fortran uses a column wise storage of matrices while C stores them
 row wise. This means that when you want to pass a matrix from your
 C-program to the fortran routine you must transpose the matrix
 in your program before entering the routine. Of course, any output
 from such a routine must be transposed again. 

 If you omit this step, then probably your program will run (because
 it has data to compute on) but it will generate wrong answers.

 Transposition is expensive, you may avoid it by adapting the source
 code, when the FORTRAN source says A(j+1,i+1) it could mean a[i][j] in C,.

 There are times when you don't want to modify already existing FORTRAN 
 and C code, in such cases you may have to write a transposition wrapper.
 This can be advisable for reasons of clarity (i.e. keeping the 
 documentation the code and the math in sync.)


 Array indexes
 -------------
 Remember that array indexes in Fortran starts by default at 1 while 
 in C they start at index 0; hence a passed array fortran_array[1:100]
 must be used in the C-routine/program as c_array[0:99].


 Variable type matching
 ----------------------
 Watch out especially with float's and double's. Make sure that the
 size of the variable in the calling program is identical to the size
 in the Fortran routine: 

    float  --- REAL     (this is typical, but not always the case)
    double --- REAL*8


 Character strings
 -----------------
 FORTRAN may pass string lengths BY VALUE in the order the strings appear 
 in the argument list.  These will NOT appear in the FORTRAN argument list, 
 but will appear in the C argument list.

 You will need to take care of nulls and blanks spaces explicitly if you 
 ignore this ...


 Some untested advice
 --------------------
 If you have the Fortran source code (of any routine) then on some 
 platforms you may use compiler directives specifying that the 
 Fortran compiler should use row wise storage.  Some platforms support 
 these directives. However watch out with this if you call the same 
 routine from another Fortran routine/program.

 Matching float types is extremely important on machines with little-
 endian byte ordering. Parsing a float (C-routine) to a real*8 (Fortran) 
 number will not generate SEGV (SEGmentation Violation error) but give 
 wrong results as the data is parsed wrongly.

 Mixing I/O on the same file descriptors may be problematic.
 If you do ANY I/O in FORTRAN, you may have to use a FORTRAN main program.

 The Sun FORTRAN compiler used lex and yacc to do the conversion of a 
 run time format from a character variable.  If you use lex and yacc, 
 either rename the variables and functions or partially link before 
 you link to the FORTRAN libraries.




 An example environment: the VMS Common Language Environment
 -----------------------------------------------------------
 VMS argument passing mechanisms are standardized by the VAX/ALPHA
 procedure-calling standards, and inter-language calls are made simple,
 a few FORTRAN language extensions makes it even simpler.

 FORTRAN programs can use the 'builtin functions' to pass arguments 
 to C routines:

    %REF(argument)      Makes the argument pass by reference,
                        this is the default for numeric types.
                        Useful for strings!
    %VAL(argument)      Makes the argument pass by value. 
                        Useful for numeric types!

      program pssarg
      integer	int
      character	string*10
      int = 8
      string = 'apples'
      call cstring(%val(int), %ref(string // char(0)))
      end


   #include 

   void cstring(int num, char *string)
	{
	printf(" %d %s\n", num, string);
	return;
	}


 Another (but ugly) workaround for the FORTRAN/C string-passing 
 problem is to store the strings in BYTE arrays.


 To go a little deeper, the only technical detail you have to learn 
 is the structure of the fixed-length string descriptor used to pass 
 character data:

   struct descriptor                       /* VMS fixed length string    */
        {                                  /* descriptor used in FORTRAN */
        unsigned short  length;
        unsigned char   data_type,         /* = 14      */
                        dsc_class;         /* = 1       */
        char            *string_ptr;
        }; 


 Fortran compilers on UNIX machines don't use descriptors, instead a
 "hidden" integer argument is added to the end of the argument list,
 specifying the length of the string.

 Two VMS examples that pass an INTEGER and a CHARACTER*10 string between
 FORTRAN and C follow. All inter-language complications were 'pushed' to 
 the C code, the FORTRAN code is not 'aware' of them. 

 The hardcoding of descriptor.data_type and descriptor.dsc_class values 
 is of course bad programming practice. The proper header file (descrip.h) 
 should have been included and used.

 The dimensional information of the CHARACTER*10 is contained in the 
 descriptor, so in the argument list you have to pass only the address
 of the descriptor.


 A VMS example (FORTRAN calling C)
 ---------------------------------

      program for2c
      integer	n
      character	string*10
      n = 123
      string = 'abcdefghij'
      write(*,*) ' In fortran:       n= ', n
      write(*,*) ' In fortran:  string= ', string
      call c_routine(n, string)
      end


   #include <stdio.h>

   struct descriptor                          /* VMS fixed length string    */
        {                                  /* descriptor used in FORTRAN */
        unsigned short  length;
        unsigned char   data_type,         /* = 14      */
                        dsc_class;         /* = 1       */
        char            *string_ptr;
        }; 


   c_routine(int *n, struct descriptor *dsc)
        {
        int     len;
        char    *string;

        printf(" in C:             n= %d \n", *n);

        len = dsc->length; 
        printf(" in C:           len= %d \n", len);

        string = dsc->string_ptr;
        printf(" in C:        string= %*.*s \n", len, len, string);

        return;
        }



 Another VMS example (C calling FORTRAN)
 ---------------------------------------

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

   struct descriptor                          /* VMS fixed length string    */
        {                                  /* descriptor used in FORTRAN */
        unsigned short  length;
        unsigned char   data_type,         /* = 14      */
                        dsc_class;         /* = 1       */
        char            *string_ptr;
        }; 

   void f_routine(int *, struct descriptor *);

   main()
        {
        int                             n = 123;
        char                            string[] = "abcdefghij";
        static struct descriptor        dsc;

        printf(" in C:             n= %d \n", n);
        printf(" in C:        string= %s \n", string);

        dsc.length     = strlen(string);
        dsc.data_type  = 14;
        dsc.dsc_class  = 1;
        dsc.string_ptr = string;

        f_routine(&n, &dsc);
        }


      subroutine f_routine (n, string)
      integer	n
      character	string*(*)
      write(*,*) ' In fortran:       n= ', n
      write(*,*) ' In fortran:  string= ', string
      return
      end


Return to contents page