j3-fortran / fortran_proposals

Proposals for the Fortran Standard Committee
175 stars 14 forks source link

type(F_ptr) to simplify type(C_ptr) work #299

Open foxtran opened 1 year ago

foxtran commented 1 year ago

Dear all,

I tried to load data from type(C_ptr) into Fortran pointer arrays.

One can guess that it can be easily done by

  call c_f_pointer(recordinfo%data, DATA, recordinfo%dimensions(1:size(shape(DATA))))

where recordinfo type is defined as follows:

  type :: C_RecordInfo_t
    type(c_ptr)        :: data
    integer(c_int64_t) :: dimensions(15)
  end type C_RecordInfo_t

and DATA is a pointer:

real(8), pointer :: DATA(:,:)

or

integer, pointer :: DATA(:,:,:)

Unfortunately, when someone will try to hide c_f_pointer call, in general case he must declare all necessary types and shapes of arrays. To prove this:

  subroutine test(recordinfo, fptr)
    type(recordinfo_t) :: recordinfo
    class(*), pointer :: fptr(..)
    call c_f_pointer(recordinfo%data, fptr_i4, recordinfo%dimensions(1:size(shape(fptr_i4))))
  end subroutine test

leads to

   43 |     call c_f_pointer(recordinfo%data, fptr, recordinfo%dimensions(1:size(shape(fptr))))
Error: Assumed-rank argument at (1) is only permitted as actual argument to intrinsic inquiry functions

Then, let's try assumed-size array:

    class(*), pointer :: fptr(:)
    call c_f_pointer(recordinfo%data, fptr, recordinfo%dimensions(1:size(shape(fptr))))

And it gives the following error:

   43 |     call c_f_pointer(recordinfo%data, fptr, recordinfo%dimensions(1:size(shape(fptr))))
Error: FPTR argument at (1) to C_F_POINTER shall not be polymorphic

Then, removing polymorphic type:

    type(*), pointer :: fptr(:)

one can obtain the following error:

   40 |   subroutine test(recordinfo, fptr)
Error: Assumed-type variable fptr at (1) may not have the ALLOCATABLE, CODIMENSION, POINTER or VALUE attribute
   43 |     call c_f_pointer(recordinfo%data, fptr, recordinfo%dimensions(1:size(shape(fptr))))
Error: Assumed-type argument at (1) is not permitted as actual argument to the intrinsic c_f_pointer

I suggest adding type(F_ptr) to manage these things in a simpler way. The idea of this type is easily casting it into other Fortran pointers.

It can be defined as follows:

 type(F_ptr) :: value
 type(F_ptr) :: array_1D(:)
 type(F_ptr) :: array_AR(..) ! assumed-rank array

In the last case, rank can be modified (like rerank( type(f_ptr) :: pointer, integer :: shape(:)) ). c_f_pointer routine also can set the shape of this array. Then, this type(F_ptr) can be assigned to another Fortran pointer. For example:

  type(F_ptr) :: A_ptr(..)
  integer, pointer :: Ai_1D(:), Ai_2D(:,:)
  real, pointer :: Ar_1D(:), Ar_2D(:,:)
  call c_f_pointer(some_C_ptr, A_ptr, shape=[8]) ! so, A_ptr has rank 1
  Ai_1D = A_ptr ! ok, size(Ai_1D) == 8
  Ai_2D = A_ptr ! runtime error, ranks are not the same
  Ar_1D = A_ptr ! ok, size(Ar_1D) == 8
  Ar_2D = A_ptr ! runtime error, ranks are not the same
  ! rerank, length can not be the same
  call rerank(A_ptr, shape=[2,3]) ! now, A_ptr has rank 2
  Ai_1D = A_ptr ! runtime error, ranks are not the same
  Ai_2D = A_ptr ! ok, shape(Ai_2D) == (/ 2, 3 /)
  Ar_1D = A_ptr ! runtime error, ranks are not the same
  Ar_2D = A_ptr ! ok, shape(Ar_2D) == (/ 2, 3 /)

Then, for my case, it can be used as follows:

  function get_data_ptr(recordinfo, ndim) result(fptr)
    type(recordinfo_t) :: recordinfo
    type(F_ptr) :: fptr(..)
    call c_f_pointer(recordinfo%data, fptr, recordinfo%dimensions(1:ndim))
  end function

and, later:

  integer, pointer :: data_ptr(:,:,:,:)
  data_ptr = get_data_ptr(recordinfo, size(shape(data_ptr)))