Search code examples
oopfortranfunction-pointerspointer-to-member

Fortran: procedure pointer to method


I'm in the following situation: I have an object that must be initialized (without hurry) with some input parameter x. Then it has a method do_work that has to run (fast). Now, depending on x the do_work can be either function f1 or f2. Of course I can choose among them when do_work is called, but, since I know the choice in advance, I was thinking to use a procedure pointer.

I produced the following MWE

module delegate_m
    implicit none

    private 

    type delegate
        private

        integer :: x
        procedure(delegate_function), private, pointer :: fptr
    contains
        private

        procedure:: f2
        procedure :: f1
        procedure, public :: do_work
    end type delegate

    interface delegate
        module procedure :: init_delegate
    end interface delegate

    abstract interface
        integer function delegate_function(self,y)
            import :: delegate
            class(delegate) :: self
            integer :: y
        end function delegate_function
    end interface

    public :: delegate

    contains
        type(delegate) function init_delegate(x)
            implicit none

            integer, intent(in) :: x

            init_delegate%x = x
            
            if (modulo(x, 2) == 0) then
                init_delegate%fptr => f1
            else
                init_delegate%fptr => f2
            end if 
        end function init_delegate

        integer function f1(self,y)
            implicit none

            class(delegate) :: self
            integer :: y

            f1 = y * self%x
        end function f1

        integer function f2(self,y)
            implicit none

            class(delegate) :: self
            integer :: y

            f2 = (y ** 2) * self%x
        end function f2

        integer function do_work(self, x, y)
            implicit none
        
            class(delegate) :: self
            integer:: x, y

            do_work = self%fptr(x) - y
        end function do_work
end module delegate_m

program test
    use delegate_m
    implicit none

    type(delegate) :: d1, d2
    d1 = delegate(45)
    d2 = delegate(44)

    write (*,*) d1%do_work(2, 3)
    write (*,*) d2%do_work(2, 3)
end program test

It seems to work, but I'm rather new to (modern) Fortran and I would like to know whether I did something wrong/dangerous since I'm working with pointers. I'm also curios if that abstract interface introduces some virtual function table lookup (I do not see why it should, but I'm a newbie, as I said)


Solution

  • To answer your questions in order:

    • It doesn't look like you've done anything dangerous or wrong. This looks to me like a good use case for procedure pointers, and your implementation looks good.
    • The abstract interface is basically just defining the "type signature" of the procedure pointer. It doesn't add any overhead.
    • You will (unless it's somehow optimised out) have the overhead of a single pointer lookup every time you call fptr. This might or might not interfere with some possible compiler optimisations. It's really hard to say whether this will actually meaningfully slow anything down without just trying it to see, and running a code profiler to find out.