j3-fortran / fortran_proposals

Proposals for the Fortran Standard Committee
178 stars 15 forks source link

Arrays of procedure pointers #139

Open rjfarmer opened 4 years ago

rjfarmer commented 4 years ago

I propose adding the ability for procedure pointers to be arrays, thus making something like this valid:

procedure(afunc), pointer, dimension(:) :: funcptr

This would allow building up "lists" of functions to be created that can be passed to a function to "process", or to make it easier to call different functions based on some index value. This would also make procedure pointers similar to other objects (and pointers) like ints, floats, derived types etc which can be arrays.

Use cases:

allocate(funcptr(3))
null(funcptr)
funcptr(1) => function1
funcptr(2) => null()
funcptr(3) =>  function3

call some_routine(funcptr)
deallocate(funcptr)

subroutine some_routine(funcptr)
    do i=1,size(funcptr)
          if(associated(funcptr(i))) call funcptr(i)
    end do

Different use case (which i have in my code)

SELECT CASE (id)
case(1)
    ptr => func1
case(2)
    ptr => func2
.....

call ptr()

Which could be made cleaner with:

 ! Inside an init function
 funcptr(1) => function1
 funcptr(2) => function2   

 ! Calling code
 call funcptr(id)

Currently you can do something similar if you do

type mytype
     procedure(afunc), pointer :: ptr
end type mytype

type(mytype), allocatable, dimension(:) :: funcptr

But this just hides whats going on and is no-obvious to a user/(me) why the pointer needs to be put inside a derived type (when you can have real, pointer, dimension(:) ).

sblionel commented 4 years ago

This would be awkward, since one can't have arrays of data pointers. This suggestion just carves out a special case and doesn't add anything you can't do in a reasonable fashion today. Yes, using a derived type is a bit weird, but I wouldn't be in favor of special-casing procedure pointers for this.

difference-scheme commented 4 years ago

Please consider also the following. In a polymorphic language (which Fortran now is) users do not actually need to be able to directly access raw procedure pointers at all (see Java as a case in point, which does not expose procedure pointers to the user).

Polymorphism provides a much safer alternative path to the same functionality (as it relies on procedure pointers being used under the hood). The fact that Fortran follows the C++ example to also make procedure pointers directly accessible to the user might arguably be seen as a weak point of both these languages in terms of safety.

ghwilliams commented 4 years ago

What about arrays of pointers for derived types. For example: type, abstract :: base_class end type base_class type, extends (base_class) :: class_a end type class_a type, extends (base_class) :: class_b end type class_b

... later

! currently allocatable conflicts with pointer. In a new standard it would be understood as an array of pointers. class (base_class), allocatable, pointer :: obj_list(:)

allocate(obj_list(2)) allocate(obj_list(1), source = class_a) allocate(obj_list(2), source = class_b)

this is the basic idea.

thoth291 commented 4 years ago

@rjfarmer for functions it could be done like this:

      interface
         function proc_int()
            real :: proc_int
         end function proc_int
      end interface

      type proc_pointer
         sequence
         procedure(proc_int),pointer, nopass :: ptr
      end type

      type(proc_pointer),dimension(1:NFUNKS) :: procs

      real function custom_func(n)
         integer :: n
         double precision :: tsum
         custom_func = 0.0D0
         tsum = 0.0D0
         do k = 1,n
            tsum = tsum + k
         enddo
         custom_func = tsum
         return
      end function custom_func

      i=0 !for example
      procs(i)%ptr=>custom_func

Haven't tried it for subroutines, though...

difference-scheme commented 2 years ago

This would be awkward, since one can't have arrays of data pointers.

Well, that exactly is the deeper issue. Not having the possibility to declare arrays of any pointers is one of the biggest deficiencies of the language! See my comment on Issue #197.

This suggestion just carves out a special case and doesn't add anything you can't do in a reasonable fashion today. Yes, using a derived type is a bit weird, but I wouldn't be in favor of special-casing procedure pointers for this.

What is truly needed is a general mechanism for declaring arrays of pointers, that must include arrays of polymorphic variables as a special case (since such variables use procedure pointers under the hood, as I stated above).

Hence, @rjfarmer's point is valid, even though I'd recommend to always prefer polymorphic variables/objects over raw procedure pointers in a polymorphic language (For reasons of safety, as this is why run-time polymorphism/OOP was invented in the first place. Procedure pointers are completely redundant in modern Fortran, and it was a very bad idea to include them in the language in the first place.).