[comp.lang.c] mixing fortran and c

duns1222@w203zrz.zrz.tu-berlin.de (Martin Dunschen) (02/15/91)

Hi all you high experienced C/FORTRAN programmers !

We have a problem which is probably solved by some of you.
We try to combine Fortran and C. All these things in Fortran, 
like COMMON Blocks, alternate returns, call by reference vs. call by value
are no problems to us anymore.

But the last remaining thing is:

      HOW TO PASS STRINGS ?

It seems, that the Fortran subroutine does not know, how long 
are the given strings. Below are a few programs for testing purpose, two
C-procedures and a fortran subroutine.

The solution is perhaps something like giving the length of the strings in a way
to the subroutine, or adding a special character, which tells the subroutine 
where the string stops.

Thanks in advance, Martin.

+---------------------------------------------------------------------------+
|     e-mail : dunschen@zrzsp9.fb12.tu-berlin.de                            |
|                                                                           |
|     Martin Dunschen                                                       |
|     Institute of Naval Architecture                                       |
|     Salzufer 17-19                                                        |
|     D-1000 Berlin 10                                                      |
|     Germany                                                               |
|                                                                           |
+---------------------------------------------------------------------------+
CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE --- CUT HERE --- 
#include <string.h>
/* main program for testing the passing of strings from c to fortran */

main ()
{
	strtst ("TESTING","CASE");
}




/* procedure, called by main, parameters are the strings */

strtst (str1, str2)
char* str1;
char* str2;
{
	char *stest1, *stest2;
       
	printf ("str1= %s\n str2= %s\n", str1, str2);
	printf ("length str1 %d\n", strlen( str1));
	printf ("length str2 %d\n", strlen( str2));

        getchar ();

/* Try to add a sign to the strings, so that FORTRAN knows where to stop */
        strcat (str1, "\n");
        strcat (str2, "\n");

	printf ("str1= %s ", str1);
	printf ("str2= %s ", str2);

        getchar ();

/* call the fortran subroutine, passing pointers to strings */

        strf_ (str1, str2);
}

c------ CUT HERE ------
       subroutine strf (str1, str2)

       character *(*) str1, str2
       print*, 'strf : ', str1, len(str1)
       print*, 'strf : ', str2, len(str2)
       end
   

dik@cwi.nl (Dik T. Winter) (02/16/91)

In article <160@mailgzrz.tu-berlin.de> duns1222@w203zrz.zrz.tu-berlin.de (Martin Dunschen) writes:
 > Hi all you high experienced C/FORTRAN programmers !
Do I qualify?
 > 
 > But the last remaining thing is:
 >       HOW TO PASS STRINGS ?
 > 
The only general answer is that the Fortran routines should not look at
the parameters as strings, but as character array's (i.e. to determine
the number of characters the routine should loop through the array
searching for a terminator, which is not provided automagically in Fortran).
If this does not satisfy you, more information is required: platform, fortran
compiler.  The answer can range from simple to impossible.  (E.g. on Alliant
systems Fortran passes the lengths of strings as parameter numbers -1, -2,
etc.)
--
dik t. winter, cwi, amsterdam, nederland
dik@cwi.nl

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (02/18/91)

In article <160@mailgzrz.tu-berlin.de>, duns1222@w203zrz.zrz.tu-berlin.de (Martin Dunschen) writes:
> Hi all you high experienced C/FORTRAN programmers!
> 
> We have a problem which is probably solved by some of you.
>       HOW TO PASS STRINGS ?

The answer is "it depends".  Which C are you using and which Fortran?
For UNIX, AEGIS, and VMS, the answer is in (Fortran documentation,
C documentation, C documentation) -- from memory.  (Yes, the answer
really is there in a complete UNIX documentation set.  Or use f2c.)
The usual technique in UNIX is to pass an extra int parameter for each
character*(_) variable giving the length for that variable, after
all the other parameters. 
-- 
Professional programming is paranoid programming

barrett@Daisy.EE.UND.AC.ZA (Alan P. Barrett) (02/18/91)

In article <2952@charon.cwi.nl>,
dik@cwi.nl (Dik T. Winter) writes:
> In article <160@mailgzrz.tu-berlin.de>
> duns1222@w203zrz.zrz.tu-berlin.de (Martin Dunschen) writes:
>  > Hi all you high experienced C/FORTRAN programmers !
>  [...]
>  > But the last remaining thing is:
>  >       HOW TO PASS STRINGS ?
>  > 
> The answer can range from simple to impossible.

Here are the four methods I have seen:

a) A string passed from a Fortran routine to a C routine appears to the
   C routine as if it were a struct.  This struct contains the string
   length and a pointer to the actual characters, and possibly some
   other stuff as well.

b) Like (a), but a pointer to the struct is passed.

c) A string passed from a Fortran routine to a C routine appears to the
   C routine as if it were two consecutive arguments:  a pointer to the
   data followed by an integer length.  (This might actually be
   indistinguishable from method (a) on some systems.)

d) Like (c), but all the Fortran string length arguments are collected
   at the end of the argument list, after all the other arguments.

To clarify what I mean, here is an example.

If you have a Fortran routine that wants to call a subroutine
declared like this:

    SUBROUTINE SUBR (I, C1, J, C2)
    INTEGER I, J
    CHARACTER *(*) C1, C2

then the subroutine can possibly be replaced by a C routine like one of
these:

    /* method (a) */
    void subr (int i, struct ftnstring c1, int j, struct ftnstring c2);

    /* method (b) */
    void subr (int i, struct ftnstring *c1, int j, struct ftnstring *c2);

    /* method (c) */
    void subr (int i, char* c1data, int c1length, int j, char* c2data,
		int c2length);

    /* method (d) */
    void subr (int i, char* c1data, int j, char* c2data, int c1length,
		int c2length);


I am appending some sample code that I have used to convert between C
and Fortran strings.

ftntypes.h declares how the Fortran system represents strings.  Find out
how your system does it, and edit this file appropriately.

ftnstring.c contains the fstringtoc() and cstringtof() functions, which
do low level conversion between the two string types, truncating or
blank-padding as necessary.  These are intended to be called from a C
routine which is in turn called from Fortran.

ftngetterm.c contains a Fortran-callable function to get the name of the
terminal from which the program was invoked.

--apb
Alan Barrett, Dept. of Electronic Eng., Univ. of Natal, Durban, South Africa
Internet: barrett@ee.und.ac.za (or %ee.und.ac.za@saqqara.cis.ohio-state.edu)
UUCP: m2xenix!quagga!undeed!barrett    PSI-Mail: PSI%(6550)13601353::BARRETT

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Alan P Barrett <barrett@undeed> on Mon Feb 18 12:48:09 1991
#
# This archive contains:
#	ftntypes.h	ftnstring.c	ftngetterm.c	
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - ftntypes.h
cat >ftntypes.h <<'@EOF'
/* @(#)ftntypes.h 1.5 90/02/01 */
/* ftntypes.h
 * A.P. Barrett
 * defines types for C/Fortran interface
 */

/********* FORTRAN character variables ********/

/* Define preprocessor symbol F_CHR_STRUCT or F_CHR_STRUCTP if Fortran passes
 * each character arg as an item of type "ftn_character".  Use F_CHR_STRUCT
 * if this item is in fact a structure, or F_CHR_STRUCTP if the item is
 * a pointer to a structure.
 *
 * Define F_CHR_LENARG or F_CHR_LENARG_END if Fortran passes two arguments,
 * a char pointer and an integer length, for each character string.  Use
 * F_CHR_LENARG if the lengths are intermixed with the string pointers, or
 * F_CHR_LENARG_END if all the lengths are grouped together at the end of the
 * argument list.
 */

#if defined(hp9000s300)
#  define F_CHR_LENARG_END
#endif
#if defined(hp9000s500)
#  define F_CHR_STRUCT
#endif
#if defined(hp9000s800)
#  define F_CHR_LENARG /* could use F_CHR_STRUCT instead ??? */
#endif

/* If Fortran passes a structure (or pointer to a structure) define it here.
 * Structure members length and start must be the length and address of the
 * string.
 */

#if defined(F_CHR_STRUCT) || defined(F_CHR_STRUCTP)
	typedef struct {
#  if defined(hp9000s500)
		long junk1,junk2,junk3;	/* I don't know what this is */
		long length;	/* length of string */
		char *start;	/* pointer to start of actual data */
#  endif
#  if defined(hp9000s800)
		int length;	/* length of string */
		char *start;	/* pointer to start of actual data */
#  endif
	}
#  if defined(F_CHR_STRUCT)
	    ftn_character;
#  else /* F_CHR_STRUCTP */
	    * ftn_character;
#  endif
#endif

/********** FORTRAN logical variables *************/

typedef long ftn_logical;
#define ftn_TRUE	((ftn_logical)~0)  /* any non-zero value will do */
#define ftn_FALSE	((ftn_logical)0)

/********** FORTRAN integer, real, doubleprecision, complex *************/

typedef long ftn_integer;
typedef float ftn_real;
typedef double ftn_doubleprecision;
typedef struct {
	ftn_real  re,im; /* real and imaginary parts */
	} ftn_complex;
@EOF

chmod 444 ftntypes.h

echo x - ftnstring.c
cat >ftnstring.c <<'@EOF'
/* ftnstring.c
 * A P Barrett, 1986
 * conversions between fortran and C strings
 *
 * fstringtoc (fstring_start, fstring_length, cstring, cstring_length)
 *     converts a Fortran string to a C string, and returns a pointer to the
 *     C string.  The C string is truncated if fstring_length is larger then
 *     cstring_length-1.
 *
 * cstringtof (cstring, fstring_start, fstring_length)
 *     converts a C string to a Fortran string, and returns the length of the
 *     Fortran string, excluding additional trailing blanks.  The Fortran
 *     string is truncated or blank filled as appropriate.
 */

char *fstringtoc (fstring_start, fstring_length, cstring, cstring_length)
char *fstring_start;
int fstring_length;
char *cstring;
int cstring_length;
{
  int i;

  /* first copy the contents of the fortran string */
  for ( i=0 ; i<fstring_length && i <cstring_length ; i++ )
  {
    *(cstring+i) = *(fstring_start+i);
  }
  /* now put a '\0' at the end */
  *(cstring+i) = '\0';
  /* and return the result */
  return (cstring);
}


int cstringtof (cstring, fstring_start, fstring_length)
char *cstring;
char *fstring_start;
int fstring_length;
{
  int i;
  char ch;
  int length;

  /* copy characters until end of fortran string or null seen */
  for ( i=0 ; (i<fstring_length) && ((ch= *(cstring+i))!='\0') ; i++ )
  {
    *(fstring_start+i) = ch;
  }
  length = i;

  /* blank fill the fortran string if necessary */
  for ( ; i<fstring_length ; i++)
  {
    *(fstring_start+i) = ' ';
  }

  return (length);
}
@EOF

chmod 444 ftnstring.c

echo x - ftngetterm.c
cat >ftngetterm.c <<'@EOF'
/* ftngetterm.c
 * A P Barrett, 1987
 * return term name to a fortran prog
 */

#include "ftntypes.h"

int cstringtof(); /* convert char array to fortran string */

char *ttyname(); /* get terminal name */
int isatty();

/* this function returns the terminal name in a fortran
 * character variable.
 * call from fortran:
 *	character*(*) termname
 *      integer length
 *	length = gettermname (termname)
 */
#ifdef F_CHR_STRUCT
  int gettermname (termname)
  ftn_character termname;
#  define termname_start termname.start
#  define termname_length termname.length
#endif
#ifdef F_CHR_STRUCTP
  int gettermname (termname)
  ftn_character termname;
#  define termname_start termname->start
#  define termname_length termname->length
#endif
#ifdef F_CHR_LENARG
  int gettermname (termname_start,termname_length)
  char *termname_start;
  int termname_length;
#endif
#ifdef F_CHR_LENARG_END
  int gettermname (termname_start,termname_length)
  char *termname_start;
  int termname_length;
#endif
{
  int length;
  char *name;
  int num;

  /* if any of stderr, stdin or stdout is a terminal, get its name */
  if (isatty(num=2) || isatty(num=0) || isatty(num=1)) {
    name = ttyname(num);
  }
  else {
    name = "??";
  }

  length = cstringtof ( name, termname_start, termname_length );
  return (length);
}
@EOF

chmod 444 ftngetterm.c

exit 0

jlg@lanl.gov (Jim Giles) (02/20/91)

From article <2952@charon.cwi.nl>, by dik@cwi.nl (Dik T. Winter):
> In article <160@mailgzrz.tu-berlin.de> duns1222@w203zrz.zrz.tu-berlin.de (Martin Dunschen) writes:
>  > But the last remaining thing is:
>  >       HOW TO PASS STRINGS ?
>  > 
> The only general answer is that the Fortran routines should not look at
> the parameters as strings, but as character array's (i.e. to determine
> the number of characters the routine should loop through the array
> searching for a terminator, which is not provided automagically in Fortran).
> [...]

Neither Fortran nor C provide automatic variable length strings.  In
Fortran, character values are passed as packed arrays of characters
(although, the degree of packing, is implementation defined).  In C,
character values are passed as a pointer to a single character (which
may be the _whole_ argument string, may be null terminated, may be
the first of a fixed length array, etc.).  In Fortran, you do _not_
need to loop through to find a terminator, since the length is passed
implicitly and can be found with the LENGTH() function.  Unfortunately,
this is the length of the _whole_ character variable and not just the
part that you're interested in - but C has the same problem in many of
its contexts: the only _automatically_ provided terminators are those
provided by the character literals (or, by the inefficient str???()
functions).  C has additional problems (like, it doesn't automatically
pass the length of the whole variable - so there's a tendency to overrun
strings in C).

J. Giles