Skip to content

Procedure pointer in variable-definition context #358

@ivan-pi

Description

@ivan-pi

I thought the following type of pattern was allowed:

module action_dict_mod
  implicit none

  abstract interface
    subroutine doit_fn()
    end subroutine
  end interface

  type :: action
    character(len=:), allocatable :: key
    procedure(doit_fn), pointer, nopass :: doit => null()
  end type

  type :: action_dict
    integer :: n = 0, capacity = 0
    type(action), allocatable :: v(:)
  contains
    procedure :: set => set_action
    procedure :: get => get_action
  end type

contains

  function set_action(dict,key) result(p)
    class(action_dict), intent(inout), target :: dict
    character(len=*), intent(in) :: key
    procedure(doit_fn), pointer :: p

! Initialization and resizing logic
    if (.not. allocated(dict%v)) then
        dict%capacity = 10
        allocate(dict%v(dict%capacity))
    else if (dict%n >= dict%capacity) then
        ! Simple doubling of capacity if full
        block
            type(action), allocatable :: temp(:)
            associate(new_capacity => dict%capacity * 2)
                allocate(temp(new_capacity))
                temp(1:dict%n) = dict%v
                call move_alloc(temp, dict%v)
                dict%capacity = new_capacity
            end associate
        end block
    end if

    ! Add new entry
    dict%n = dict%n + 1
    dict%v(dict%n)%key = trim(key)

    ! Return the pointer to the procedure inside the dictionary
    p => dict%v(dict%n)%doit

  end function


  function get_action(dict,key) result(p)
    class(action_dict), intent(in) :: dict
    character(len=*), intent(in) :: key
    procedure(doit_fn), pointer :: p

    integer :: i

    p => null()

    ! Naive linear search
    do i = 1, dict%n
        if (dict%v(i)%key == trim(key)) then
            p => dict%v(i)%doit
            return
        end if
    end do

  end function

end module

program main
  use action_dict_mod
  implicit none

  type(action_dict) :: my_actions
  procedure(doit_fn), pointer :: proc_ptr

  ! Using the F2008 pointer-function result to assign the procedure
  my_actions%set("hello") => hello_sub

  ! Retrieve and execute
  proc_ptr => my_actions%get("hello")
  if (associated(proc_ptr)) call proc_ptr()

contains

  subroutine hello_sub()
    print *, "Hello, World!"
  end subroutine

end program main

It seems like there is a restriction on this. The Intel Fortran documentation says under "Disallowed Contexts":

For the following variable-definition contexts, the Fortran Standard specifies that a "variable name" must be used and not a "variable":

  • [...]
  • A data pointer object or procedure pointer object in a pointer assignment statement

This appears related to (J3/24-007, 10.2.2.2):

R1034 pointer-assignment-stmt is ...
                              or proc-pointer-object => proc-target

R1039 proc-pointer-object     is proc-pointer-name
                              or proc-component-ref
R1040 proc-component-ref      is scalar-variable % procedure-component-name

Is there any reason why an expr could not be added to R1039? The constraints on expr would be the same as,

C1032 (R1041) An expr shall be a reference to a function whose result is a procedure pointer.

The design is still achievable today, by using a wrapper type for the procedure pointer, effectively changing the pointer assignment into a data-pointer assigment.

As an implementation-note, this usage pattern also benefits from the extension proposed in #354.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions