I am new to fortran programming.. I have a 3 dimensional array declared as follows
REAL*4, DIMENSION(:,:,:), ALLOCATABLE :: a1
I want to pass the array by reference to a C or C++ function and allocate memory in C rather than in fortran. Is it possible or I am wrong in understanding the array concept in fortran ?
To allocate memory for an array, just multiply the size of each array element by the array dimension. For example: pw = malloc(10 * sizeof(widget)); assigns pw the address of the first widget in storage allocated for an array of 10 widget s.
A dynamic array is an array, the size of which is not known at compile time, but will be known at execution time. Dynamic arrays are declared with the attribute allocatable. The rank of the array, i.e., the dimensions has to be mentioned however, to allocate memory to such an array, you use the allocate function.
The ALLOCATE statement dynamically creates storage for array variables having the ALLOCATABLE or POINTER attribute. If the object of an ALLOCATE statement is a pointer, execution of the ALLOCATE statement causes the pointer to become associated.
It depends, but probably not, depending on your circumstances. Definitely not in a portable way, if you are limited to Fortran 90 as per the question tag.
Note that your Fortran variable is an ALLOCATABLE.
As you've asked, it requires a Fortran compiler (and companion C compiler) that implement the relatively recently published Technical Specification (TS 29113:2012). You are operating at the bleeding edge of the development of the language - and the number of compilers that currently support this TS is small (if not zero). It is expected that the contents of this TS would be incorporated into next revision of the Fortran standard, so try again in a few years time and you might have better luck.
Note though, that the TS limits the way in which C can do the allocation for Fortran - basically the C code needs to call a Fortran compiler supplied routine. You can not use any old allocator (including malloc), so this still may not suit.
However, Fortran 2003 added the possibility for the memory allocation of Fortran POINTER's (not allocatables as you've asked) to be done in C. The C code returns a pointer to a chunk of memory of the appropriate size, which the Fortran procedure receives back as object of type C_PTR. The Fortran code then calls the procedure C_F_POINTER to associate the C address in the C_PTR object with the Fortran pointer. C_PTR and C_F_POINTER are entities in the intrinsic ISO_C_BINDING module.
A crude example:
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER
INTERFACE
FUNCTION get_some_memory() BIND(C, NAME='get_some_memory')
IMPORT :: C_PTR
TYPE(C_PTR) :: get_some_memory
END FUNCTION get_some_memory
END INTERFACE
! REAL*4 isn't Fortran. Correct syntax below.
REAL(4), POINTER, DIMENSION(:,:,:) :: a1
! get_some_memory would typically be told how much memory was needed.
! n1, n2, n3 are integer with the relevant extents for each dimension.
CALL C_F_POINTER(get_some_memory(), a1, [n1,n2,n3])
! Equivalent call to free required at some later stage.
void *get_some_memory()
{
return malloc(some_number_of_bytes);
}
Beyond this, there are processor dependent (i.e. non-portable) tricks that you can play, but they are outside of the standard language.
As the answer by IanH says, the Technical Specification TS 29113 allows a way for the allocation of a Fortran allocatable object to be performed in a C function, this having a lasting effect on the Fortran side.
Since that earlier answer was written support for this TS has increased (although it's still not large and the C descriptor part we'll use in this answer is notably not supported by current gfortran). That means it's now probably worth writing an example. This example is not valid under Fortran 2008.
We consider the C structure given by CFI_desc_t
(from the source file ISO_Fortran_binding.h
) and the C function CFI_allocate
.
Assume that our allocation of the rank-3 array occurs in a C procedure compatible with the following Fortran interface
interface
subroutine allocating_sub(x) bind(c)
use, intrinsic :: iso_c_binding, only : c_float
real(c_float), allocatable :: x(:,:,:)
end subroutine allocating_sub
end interface
We note that such a Fortran interface is compatible with a C function prototype which has the formal argument as a pointer to CFI_cdesc_t
.
Define this C function as (with magic numbers, and missing lots of other sensible checking and such):
#include "ISO_Fortran_binding.h"
void allocating_sub(CFI_cdesc_t* x) {
if (x->base_addr) CFI_deallocate(x);
CFI_index_t lower[3]={-1, 5, 1};
CFI_index_t upper[3]={20, 9, 5};
CFI_allocate(x, lower, upper, 0);
}
CFI_index_t
is a "typedef name for a standard signed integer type capable of representing the result of subtracting two pointers".
Allocation happens with the CFI_allocate
function which here allocates x
with the lower and upper bounds given by the obvious variables. The final number (0
) is ignored in our case with x
not corresponding to a Fortran character type.
To complete the example, take the Fortran program:
use, intrinsic :: iso_c_binding, only : c_float
implicit none
interface
subroutine allocating_sub(x) bind(c)
import c_float
real(c_float), allocatable :: x(:,:,:)
end subroutine allocating_sub
end interface
integer i
real(c_float), allocatable :: x(:,:,:)
allocate(x(6,2,5))
print 1, (LBOUND(x,i), UBOUND(x,i), i=1,3)
call allocating_sub(x)
if (allocated(x)) print 1, (LBOUND(x,i), UBOUND(x,i), i=1,3)
1 format ("x has been allocated like x(",2(I0,":",I0,","),I0,":",I0,").")
end
The example is a little non-minimal as it also shows the CFI_deallocate
function and the fact that, although we have this extra level on the C side, we can still access the memory in that C function through the component x->base_addr
. This is a null pointer if the Fortran variable is not allocated; otherwise it's the C address of the first element of the Fortran array, as long as there is at least one element.
Much more detail can be found in the TS document or the draft upcoming Fortran standard.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With