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.

 One solution is to use the amazing 'cfortran' package, it takes some 
 effort to study the documentation, and may overtax your compiler.

 
 Cfortran package
 ----------------
 The cfortran package attempts to make Fortran/C interfacing "quick 
 and easy" for a wide range of platforms.  The most recent version 
 of the include file "cfortran.h", which does the work, and the 
 documentation is available via anonymous ftp at:

    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 the FORTRAN compiler appends a trailing underscore 
 to FORTRAN routine names both in routine definitions and in CALL 
 statements. 

 Why adding the underscore suffix? possible explanations are:

   1) Prevent possible name clashes of user-written 
      routines with the routines in the system library 
      used by the compiler (if the routine names in 
      the library doesn't have a trailing underscore). 
      The system routines on UNIX have simple names 
      that are "tempting" to use in a user program.

   2) Prevent "naive" attempts to do mixed-language
      programming. Without real understanding such 
      attempts are doomed from the start.

 In mixed-language programs on such machines, the linker will have a 
 problem when matching routine calls with routine code in the object
 code. Either the FORTRAN CALL statement (with underscores appended) 
 may 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, that can be avoided by manually
 supplying the required underscores:

   1) 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.

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

 Such compilers 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). 
 This means that you MUST give addresses in your calling C-program.


 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.


 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
C     ------------------------------------------------------------------
      INTEGER
     *				INT
C     ------------------------------------------------------------------
      CHARACTER
     *				STRING*10
C     ------------------------------------------------------------------
      INT = 8
      STRING = 'Apples'
      CALL cstring(%VAL(INT), %REF(STRING // CHAR(0)))
C     ------------------------------------------------------------------
      END


#include 

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


 Another 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;
        }; 


 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 FOR_C
C     ------------------------------------------------------------------
      INTEGER
     *                  N
C     ------------------------------------------------------------------
      CHARACTER
     *                  STRING*10
C     ------------------------------------------------------------------
      N = 123
      STRING = 'abcdefghij'
C     ------------------------------------------------------------------
      WRITE(*,*) ' IN FORTRAN:       N= ', N
      WRITE(*,*) ' IN FORTRAN:  STRING= ', STRING
C     ------------------------------------------------------------------
      CALL c_routine(N, STRING)
C     ------------------------------------------------------------------
      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_RTN(&n, &dsc);
        }


      SUBROUTINE F_RTN (N, STRING)
C     ------------------------------------------------------------------
      INTEGER
     *                  N
C     ------------------------------------------------------------------
      CHARACTER
     *                  STRING*(*)
C     ------------------------------------------------------------------
      WRITE(*,*) ' IN FORTRAN:       N= ', N
      WRITE(*,*) ' IN FORTRAN:  STRING= ', STRING
C     ------------------------------------------------------------------
      RETURN
      END


Return to contents page