From: koentjepoppe on
Dear all,

I have a problem that can be reduced to the following situation:

- mainf.f90 (see below)
a Fortran function that returns an array containing
n-copy's of a given double precision number.
- mainc.c (see below)
a C-file that would like to use this function.

The problem is that it doesn't work (Bus Error on assigment of
array(I) in the Fortran code). One way or another, the output argument
is not available in the Fortran code.

I know one solution would be to change the function into a subroutine,
adding the result as an extra argument, but this would spoil the whole
design of the project. So I'd like to know if anyone has an
alternative way to solve this.

Best Regards

ps: for the sake of completeness: I'm using the latest stable
gFortran, gcc 4.3 and I'm working on a Intel Mac.

[1]: http://docs.sun.com/source/806-3593/11_cfort.html


---8<---- MAINF.F90 ---8<-------8<-------8<----
function repeatdouble( N, d ) result( array )
integer, intent( in ) :: N
double precision, dimension( N ) :: array
double precision, intent( in ) :: d

print *, "repeat ", N, " times ..."
print *, "double ", d, "."
print *, "array :", array
do I = 1,N
print *, "making copy nb", i, " of ", N
array(I) = d
end do
end function repeatdouble
--->8------->8------->8------->8------->8------->8----



---8<---- MAINC.C ---8<-------8<-------8<----
#include <stdio.h>
extern void repeatdouble_( double*, int*, double* );
int main(){

double buf[9] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
double* ptr2buf = buf;
double d = 3.141592654;
int n = 4;
int i;

// make n copies of ch in sbf
repeatdouble_( ptr2buf, &n, &d );

for( i=0; i<9; i++){
printf("buf[%i]=%d\n", i, buf[i]);
}
}
--->8------->8------->8------->8------->8------->8----
From: glen herrmannsfeldt on
koentjepoppe(a)gmail.com wrote:

> I have a problem that can be reduced to the following situation:

> - mainf.f90 (see below)
> a Fortran function that returns an array containing
> n-copy's of a given double precision number.
> - mainc.c (see below)
> a C-file that would like to use this function.

> The problem is that it doesn't work (Bus Error on assigment of
> array(I) in the Fortran code). One way or another, the output argument
> is not available in the Fortran code.

Since C doesn't have array valued functions, this isn't going
to be easy. You can have functions returning a struct containing
an array, though the array must be fixed size.

> I know one solution would be to change the function into a subroutine,
> adding the result as an extra argument, but this would spoil the whole
> design of the project. So I'd like to know if anyone has an
> alternative way to solve this.
(snip)

> ---8<---- MAINF.F90 ---8<-------8<-------8<----
> function repeatdouble( N, d ) result( array )
> integer, intent( in ) :: N
> double precision, dimension( N ) :: array
> double precision, intent( in ) :: d
>
> print *, "repeat ", N, " times ..."
> print *, "double ", d, "."
> print *, "array :", array
> do I = 1,N
> print *, "making copy nb", i, " of ", N
> array(I) = d
> end do
> end function repeatdouble

> ---8<---- MAINC.C ---8<-------8<-------8<----
> #include <stdio.h>
> extern void repeatdouble_( double*, int*, double* );

This is for a three argument subroutine, not a two argument
function. It is possible that the calling sequence is the same,
but that is not standard.

> int main(){
> double buf[9] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
> double* ptr2buf = buf;
> double d = 3.141592654;
> int n = 4;
> int i;

> // make n copies of ch in sbf
> repeatdouble_( ptr2buf, &n, &d );

This agrees with a call to a Fortran subroutine, not a function.

> for( i=0; i<9; i++){
> printf("buf[%i]=%d\n", i, buf[i]);
> }
> }

From: James Van Buskirk on
<koentjepoppe(a)gmail.com> wrote in message
news:80a7c031-33c1-4463-a60f-b4125c033a02(a)l64g2000hse.googlegroups.com...

> I have a problem that can be reduced to the following situation:

> - mainf.f90 (see below)
> a Fortran function that returns an array containing
> n-copy's of a given double precision number.
> - mainc.c (see below)
> a C-file that would like to use this function.

> The problem is that it doesn't work (Bus Error on assigment of
> array(I) in the Fortran code). One way or another, the output argument
> is not available in the Fortran code.

> I know one solution would be to change the function into a subroutine,
> adding the result as an extra argument, but this would spoil the whole
> design of the project. So I'd like to know if anyone has an
> alternative way to solve this.

> ps: for the sake of completeness: I'm using the latest stable
> gFortran, gcc 4.3 and I'm working on a Intel Mac.

> [1]: http://docs.sun.com/source/806-3593/11_cfort.html

> ---8<---- MAINF.F90 ---8<-------8<-------8<----
> function repeatdouble( N, d ) result( array )
> integer, intent( in ) :: N
> double precision, dimension( N ) :: array
> double precision, intent( in ) :: d

> print *, "repeat ", N, " times ..."
> print *, "double ", d, "."
> print *, "array :", array
> do I = 1,N
> print *, "making copy nb", i, " of ", N
> array(I) = d
> end do
> end function repeatdouble
> --->8------->8------->8------->8------->8------->8----

> ---8<---- MAINC.C ---8<-------8<-------8<----
> #include <stdio.h>
> extern void repeatdouble_( double*, int*, double* );
> int main(){

> double buf[9] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
> double* ptr2buf = buf;
> double d = 3.141592654;
> int n = 4;
> int i;

> // make n copies of ch in sbf
> repeatdouble_( ptr2buf, &n, &d );

> for( i=0; i<9; i++){
> printf("buf[%i]=%d\n", i, buf[i]);
> }
> }
> --->8------->8------->8------->8------->8------->8----

The first argument to repeatdouble should not be the address
of an array, rather that of the array descriptor.
Disassembling a gfortran call to a similar function, we get
something like:

type, bind(C) :: descr
integer(C_INTPTR_T) address
integer(C_INTPTR_T) unknown1
integer(C_INTPTR_T) unknown2
integer(C_INTPTR_T) stride
integer(C_INTPTR_T) unknown3
integer(C_INTPTR_T) last_element
end type descr
descr DD
double precision, target :: buf(9)
double precision d
integer n

buf = [(n,n=1,9)]
d = 3.141592654
n = 4

DD = descr(C_LOC(buf), 0, 537, 1, 0, n-1)
call repeatdouble_(descr, n, d)

But you should ask the gfortran folks if they can round up some
documentation about gfortran array descriptors for you. They
are different in every compiler.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


From: FX on
> But you should ask the gfortran folks if they can round up some
> documentation about gfortran array descriptors for you. They are
> different in every compiler.

Yep, that's on the TODO list. Here's the short version (in C syntax).

The descriptor for a given type (named here TYPE) is then:

struct {
TYPE *data;
size_t offset;
ssize_t dtype;
descriptor_dimension dim[7];
}

where descriptor_dimension is defined as

typedef struct descriptor_dimension
{
ssize_t stride;
ssize_t lbound;
ssize_t ubound;
}
descriptor_dimension;

All fields have the meaning implied by their name, and dtype is such that:

* (dtype & 0x07) is the rank
* (dtype >> 6) is the size of an array element

The data component points to the first element in the array. The offset
field is the position of the element (0, 0, ...). An element is accessed
by data[offset + index0*stride0 + index1*stride1 + ...]


All this information and more can be found in the big comment in the
source at gcc/fortran/trans-types.c, line 949 in the current sources.

--
FX
From: James Van Buskirk on
"FX" <coudert(a)alussinan.org> wrote in message
news:fudrd5$9ts$1(a)nef.ens.fr...

>> But you should ask the gfortran folks if they can round up some
>> documentation about gfortran array descriptors for you. They are
>> different in every compiler.

> Yep, that's on the TODO list. Here's the short version (in C syntax).

Thanks for the documentation, FX! Since I can never seem to get all
my ducks in a row on the first attempt, I tried a pure Fortran test
of my initial recommendation and found some syntax errors. After
fixing them up I got:

C:\gfortran\clf\repeatdouble>type repeatdouble.f90
function repeatdouble( N, d ) result( array )
use iso_c_binding
implicit none
integer, intent( in ) :: N
double precision, dimension( N ) :: array
double precision, intent( in ) :: d
integer I
print *, "repeat ", N, " times ..."
print *, "double ", d, "."
print *, "array :", array
do I = 1,N
print *, "making copy nb", i, " of ", N
array(I) = d
end do
end function repeatdouble

C:\gfortran\clf\repeatdouble>type test3.f90
program main
use ISO_C_BINDING
implicit none
type, bind(C) :: descr
type(C_PTR) address
integer(C_INTPTR_T) unknown1
integer(C_INTPTR_T) unknown2
integer(C_INTPTR_T) stride
integer(C_INTPTR_T) unknown3
integer(C_INTPTR_T) last_element
end type descr
interface
subroutine repeatdouble(DD,n,d) bind(C,name='repeatdouble_')
use ISO_C_BINDING
import descr
implicit none
type(descr) DD
integer(C_INT) n
real(C_DOUBLE) d
end subroutine repeatdouble
end interface
type(descr) DD
double precision, target :: buf(9)
double precision d
integer n

buf = [(n,n=1,9)]
d = 3.141592654
n = 4

DD = descr(C_LOC(buf), 0, 537, 1, 0, n-1)
call repeatdouble(DD, n, d)
write(*,'(9(f4.1))') buf
end program main

C:\gfortran\clf\repeatdouble>c:\gcc_equation\bin\x86_64-pc-mingw32-gfortran
-c r
epeatdouble.f90

C:\gfortran\clf\repeatdouble>c:\gcc_equation\bin\x86_64-pc-mingw32-gfortran
test
3.f90 repeatdouble.o -otest3

C:\gfortran\clf\repeatdouble>test3
repeat 4 times ...
double 3.1415927410125732 .
array :
making copy nb 1 of 4

So the test failed. I found the reason in test3.f90:

C:\gfortran\clf\repeatdouble>type test3.f90
program main
use ISO_C_BINDING
implicit none
type, bind(C) :: descr
type(C_PTR) address
integer(C_INTPTR_T) unknown1
integer(C_INTPTR_T) unknown2
integer(C_INTPTR_T) stride
integer(C_INTPTR_T) unknown3
integer(C_INTPTR_T) last_element
end type descr
interface
subroutine repeatdouble(DD,n,d) bind(C,name='repeatdouble_')
use ISO_C_BINDING
import descr
implicit none
type(descr) DD
integer(C_INT) n
real(C_DOUBLE) d
end subroutine repeatdouble
end interface
type(descr) DD
double precision, target :: buf(9)
double precision d
integer n

buf = [(n,n=1,9)]
d = 3.141592654
n = 4

DD = descr(C_LOC(buf), 0, 537, 1, 0, n-1)
! Shows the error: start
write(*,*) DD
write(*,*) C_LOC(buf)
DD%address = C_LOC(buf)
write(*,*) DD
! Shows the error: end
call repeatdouble(DD, n, d)
write(*,'(9(f4.1))') buf
end program main

C:\gfortran\clf\repeatdouble>c:\gcc_equation\bin\x86_64-pc-mingw32-gfortran
-c r
epeatdouble.f90

C:\gfortran\clf\repeatdouble>c:\gcc_equation\bin\x86_64-pc-mingw32-gfortran
test
3.f90 repeatdouble.o -otest3

C:\gfortran\clf\repeatdouble>test3
0 0 537
1 0 3
2293200
2293200 0 537
1 0 3
repeat 4 times ...
double 3.1415927410125732 .
array : 1.00000000000000000 2.0000000000000000
3.000000000000000
0 4.0000000000000000
making copy nb 1 of 4
making copy nb 2 of 4
making copy nb 3 of 4
making copy nb 4 of 4
3.1 3.1 3.1 3.1 5.0 6.0 7.0 8.0 9.0

So it seems that the structure constructor for type(descr) isn't
assigning that first member, address. If you do it by hand
everything works. This is a pretty recent version of gfortran,
so I think that the above example shows a bug in the compiler.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end