[comp.lang.fortran] FORTRAN callable C functions to allocate/deallocate arrays.

mlzerkle@athena.mit.edu (Michael L Zerkle) (11/10/90)

I am trying to develop C functions to allocate and deallocate arrays
for use in a FORTRAN progam that would run on a number of different
UNIX boxes (DEC VS3100, Apollo, SGI 4D/210, etc.).  The C functions
I have written appear to allocate the double array correctly, but
either it does not pass it back the the FORTRAN program correctly, or
the FORTRAN program is not accepting the pointer/array it is returning.

Does anyone out there have any experience with problems of this sort,
or know what I am doing wrong?  Any suggestions?

Basic info:
  	f77 compiler on SGI 4D/210
        ANSI C compiler on SGI 4D/210
	same problem on other UNIX systems.

Thanks in advance for any help.

Mike Zerkle
mlzerkle@athena.mit.edu

********************
* Fortran Code ... *
********************

      program testgetm
      real*8 a(1)
      integer*4 maxa,bytesa,i
      external getmd,freemd
c
      maxa=5
      bytesa=8
c
c Write Initial location.
c
      write(6,'(a)') 'Initial address'
      write(6,*) loc(a),maxa,bytesa
c
c Allocate real*8 array.
c
       call getmd(a,maxa)
c
c Write addresses
c
      write(6,'(a)') 'Final address'
      write(6,*) loc(a),maxa,bytesa
c
c Work on array
c
      write(6,'(a)') 'REAL*8 array '
      if (loc(a).ne.0) then
       	 do 10 i=1,maxa
c           a(i)= 1.0d+0*i
	    write(6,*) i, a(i)
 10      continue
      else
	 write(6,'(a)') ' Unable to allocate r*8 array a'
      endif
c
c Free arrays.
c
      if (loc(a).ne.0) call freemd(a)
c
      stop
      end

**************
* C Code ... *
**************

#include <stdlib.h>

void getmd_(double *array, int *nelem)
{
  printf("org array addr = %ld\n",array);
  array = (double *) calloc((size_t) *nelem, (size_t) sizeof(double));
  printf("new array addr = %ld\n",array);
}

void freemd_(double *array)
{
  free((void *) array);
}

*********************
* Sample output ... *
*********************

Initial address
  29996  5  8
org array addr = 29996
new array addr = 54272
Final address
  29996  5  8
REAL*8 array 
  1  0.
  2  0.
  3  0.
  4   1.1840974637956d+30
  5   5.2810833895564d-20

bron@bronze.wpd.sgi.com (Bron Campbell Nelson) (11/13/90)

In article <1990Nov9.210442.27086@athena.mit.edu>, mlzerkle@athena.mit.edu (Michael L Zerkle) writes:
> I am trying to develop C functions to allocate and deallocate arrays
> for use in a FORTRAN progam that would run on a number of different
> UNIX boxes (DEC VS3100, Apollo, SGI 4D/210, etc.).  The C functions
> I have written appear to allocate the double array correctly, but
> either it does not pass it back the the FORTRAN program correctly, or
> the FORTRAN program is not accepting the pointer/array it is returning.
> 
> Does anyone out there have any experience with problems of this sort,
> or know what I am doing wrong?  Any suggestions?
> 

I'll skip over the error in the code and instead mention one way that
you can do this using SGI Fortran.  It relys on a couple of things:
  (1) Fortran array parameters are passed simply by passing the address
	(no funny array descriptors or dope vectors)
  (2) The VAX Fortran "%val" extension.
These two assumptions are true with SGI Fortran, and are probably true on
a number of other platforms.  For instance, the following program will
behave like one would hope:

	integer foo
	foo = malloc(1000*1000*4)
	call fill(%VAL(foo), 1000,1000)
	end

	subroutine fill(a,i,j)
	real a(i,j)
	do n1 = 1, 1000
		do n2 = 1, 1000
			a(n2,n1) = 0.0
		enddo
	enddo
	return
	end

Some versions of the SGI software do not define a Fortran interface to
malloc.  In such a case you'll also have to write the 1 line C routine

	int malloc_(size) int *size; { return (int) malloc(*size); }


--
Bron Campbell Nelson
bron@sgi.com  or possibly  ..!ames!sgi!bron
These statements are my own, not those of Silicon Graphics.

jim@interet.UUCP (User) (11/15/90)

In article <1990Nov9.210442.27086@athena.mit.edu>, mlzerkle@athena.mit.edu (Michael L Zerkle) writes:
> I am trying to develop C functions to allocate and deallocate arrays
> for use in a FORTRAN progam that would run on a number of different
> UNIX boxes (DEC VS3100, Apollo, SGI 4D/210, etc.).
> 
>       program testgetm
>       real*8 a(1)
>
>        call getmd(a,maxa)

This call overwrites the address of  a,  which  I  believe  is  incorrect,
although  it may work on certain platforms.  The "a" address is a constant
within the meaning of Fortran.

> void getmd_(double *array, int *nelem)

It is dangerous to declare nelem an int pointer.  It is far better to  de-
clare it a long pointer, since all fortrans I know treat most variables as
four bytes, or long.  This even applies to logical.

We allocate storage for Fortran programs as follows:
        subroutine dostuf(a,maxa)
        integer maxa
        double precision a(maxa)
        ....  massive great things happen  ....
        end
The real work is done in dostuf, and Fortran expects a pointer "a" which
you provide by calling it from c.

void ccall_(nelem)      /* used to call dostuf_() with needed storage */
long *nelem;           /* size of array needed */
{
  double *array;        /* just an example, not tested */
  array = (double *) calloc((size_t) *nelem, (size_t) sizeof(double));
  dostuf_(array, nelem);
  free((void *) array);
  }

You must call ccall() from Fortran after Fortran figures out how big the
array needs to be:
      program myprog
      ...
c  we need isize elements.
      call ccall(isize)
c  large job returns.
      ...
      end

The above is highly portable, except that you have the portability problem
of  linking  c and Fortran in the first place.  Your solution is more gen-
eral and powerful, but I would be concerned with the portability.  I  hope
this helps.

In our shop, we solve the problem of linking Fortran with c first, and  we
will not use hardware for which this is not easy.  Then we solve all other
portability problems by writing in either c  or  Fortran  as  appropriate.
Storage allocation is portable in c, so c is used in the above example.

Note that f2c solves the problem of linking Fortran with c.

Jim       uunet!interet!jim