4

I have a Fortran program with an allocatable array A as follows:

real, dimension(:,:) allocatable :: A
...
allocate(A(x0:x1;y0:y1))

This array is eventually passed as argument to a subroutine which looks like

subroutine my_subroutine(arr)
   real, dimension(x0:x1,y0:y1) :: arr
   ...
end subroutine my_subroutine

I wanted to replace the allocate statement of Fortran by a custom memory allocation function my_alloc implemented in a C library. I changed the first code sample into:

type(c_ptr) :: cptr
real, pointer, dimension(:,:) :: A
...
cptr = my_alloc(...)
call c_f_pointer(cptr,A,[x1-x0+1,y1-y0+1])

This works fine, except that by specifying extents instead of lower/upper bounds in the c_f_pointer function, I lose the original shape (x0:x1,y0:y1) of the array. But this is not a big problem: the pointer is passed as argument of the subroutine, the subroutine expects an array and considers the pointer as an array, with the proper bounds.

My real problem is: when I want to also rewrite the subroutine's code to have a pointer instead of an array.

subroutine my_subroutine(arr)
   real, pointer, dimension(x0:x1,y0:y1) :: arr
   ...
end subroutine my_subroutine

The code above doesn't work; gfortran says

Array pointer 'arr' at (1) must have a deferred shape

The following code can be compiled

subroutine my_subroutine(arr)
   real, pointer, dimension(:,:) :: arr
   ...
end subroutine my_subroutine

but it doesn't provide the bounds and the program crashes when I try to perform a loop from x0 to x1 and from y0 to y1.

How can I handle this case? Within the subroutine, I need fortran to know that arr is a pointer to an array shaped (x0:x1,y0;y1).

3 Answers 3

6

Yes, this was a problem because of the limitation of c_f_pointer. As you have found the intrinsic c_f_pointer only supports bounds starting at index 1. People frequently state that Fortran is a one-indexed language but this not true. One indexing is only the default and Fortran has long supported declaring any starting bound that the programmer wants. So it was a step backwards that c_f_pointer forced you to use one indexing. But with Fortran 2003 there is a fix: pointer bounds remapping:

arr (0:n-1) => arr

instead of 1:n, or whatever you wish.

Then pass the array to the subroutine and it will receive the intended bounds.

EDIT: improve demo program showing the difference between allocatables and pointers. The pointer passes the bounds of the array. A regular array passes the shape ... you can declare the first dimension in a subroutine, if you wish, and let the shape control the second.

module mysubs

implicit none

contains

subroutine testsub ( ptr, alloc, start, array )

   real, pointer, dimension (:) :: ptr
   real, dimension (:), intent (in) :: alloc
   integer, intent (in) :: start
   real, dimension (start:), intent (in) :: array

   write (*, *) "pointer in sub:", lbound (ptr, 1), ubound (ptr, 1)
   write (*, *) ptr

   write (*, *) "1st array in sub:", lbound (alloc, 1), ubound (alloc, 1)
   write (*, *) alloc

   write (*, *) "2nd array in sub:", lbound (array, 1), ubound (array, 1)
   write (*, *) array

   return

end subroutine testsub

end module mysubs


program test_ptr_assignment

use mysubs

implicit none

real, pointer, dimension(:) :: test
real, allocatable, dimension(:) :: alloc1, alloc2
real, allocatable, dimension(:) :: alloc1B, alloc2B

allocate ( test (1:5), alloc1 (1:5), alloc1B (1:5) )
test = [ 1.0, 2.0, 3.0, 4.0, 5.0 ]
alloc1 = test
alloc1B = test

write (*, *) "A:", lbound (test, 1), ubound (test, 1)
write (*, *) test

call testsub (test, alloc1, 1, alloc1B )

test (0:4) => test
allocate ( alloc2 (0:4), alloc2B (0:4) )
alloc2 = test
alloc2B = test

write (*, *)
write (*, *) "B:", lbound (test, 1), ubound (test, 1)
write (*, *) test

call testsub (test, alloc2, 0, alloc2B)

stop

end program test_ptr_assignment
Sign up to request clarification or add additional context in comments.

4 Comments

Pointer bounds remapping seems to be what I'm looking for, yet it is not implemented in gfortran (I get an error at compile time). I'll try to find another way. Other question: if I use a pointer in a program and pass it to a subroutine that expects an array, does it matter? It seems to work fine with gfortran but I wonder if it's in the standards...
The above demo program works fine with gfortran 4.6.1. Yes, you can pass a pointer array to a subroutine dummy argument that is a plain array.
Thanks, switching from gfortran 4.4 to 4.6 solved some problems, yet pointer remapping is not possible with multidimensional arrays :(
Multi-dimensional arrays may also need the "contiguous" attribute for pointer remapping to work.
1

You can use lbound and ubound built-in functions to find out the bounds of the array within the subroutine, e.g.

program test
  real, dimension(:,:), pointer :: A

  allocate(A(3:5,7:8))
  A = 1
  call my_print(A)

contains

  subroutine my_print(X)
    integer :: i,ml,mu
    real, dimension(:,:), pointer :: X
    ml = lbound(X,1)
    mu = ubound(X,1)
    do i = ml,mu
       write(*,*) X(i,:)
    end do
  end subroutine my_print

end program test

Comments

0

The defered shape the compiler insists on means, you can not declare upper bounds of the dummy arguments. But you can declare the lower ones, and the upper ones you will get automatically:

subroutine my_subroutine(arr)
   real, dimension(x0:,y0:) :: arr
   ...
end subroutine my_subroutine

It's true also for allocatable arrays.

2 Comments

Actually when specifying only lower bounds, I still get the compiler error: Array pointer 'arr' at (1) must have a deferred shape
You are right, it is an error to do this with pointer dummy argument. But you can leave the pointer attribute out, if you don't need to reassign it. See my edited answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.