3

I am using procedure overloading and interfaces in order to achieve some sort of genericity in a Fortran program.
For this, I have a module which contains a number of procedures, all duplicated in order to be able to change the variable types. I also have at the beginning of the module, a series of interfaces of the type:

    interface norm
      module procedure &
          norm_r8, &
          norm_c
    end interface

Now my problem is that I am trying to reference norm using a procedure pointer, as such (in a different module):

procedure(), POINTER :: pNorm => NULL()
pNorm => norm

However, in this situation, gfortran gives me an error saying that I have an undefined reference to norm. If I point to norm_r8 or norm_c, no problem. But since the part of the code that assigns the pointer is not aware of the type of the variables that will be used when norm is called, I need to point to the generic name! Is there a way to point towards an overloaded procedure?

1
  • 1
    It's the same in C++ - one cannot obtain the address of an overloaded function unless it is cast to a pointer to a function with the specific signature so that the compiler can select the proper overload. And this behaviour makes perfect sense since overload resolution can only be performed at compile time in static languages like C++ and Fortran unless a mechanism similar to the virtual method table is employed. Commented Jul 30, 2014 at 21:05

2 Answers 2

4

As far as I can tell, a procedure pointer is not allowed to point at a generic interface. The standard only mentions procedures with the EXTERNAL attribute, a module procedures, or certain intrinsic procedures may be associated with a procedure pointer (C1220, ISO/IEC 1539-1:2010). Gfortran also issues a helpful error message for your case:

Error: Procedure pointer target 'norm' at (1) must be either an intrinsic, 
       host or use associated, referenced or have the EXTERNAL attribute

It also makes sense that you cannot associate to an interface, but only a procedure. An interface is only used in the procedure(INTERFACE) statement to give an explicit interface to the procedures it can point at.

This shouldn't be a showstopper for you, as the purpose of a generic interface can negate your need for a pointer. As long as all potential calls the pointer would be used for are unique in type, kind, rank and number of arguments (so the compiler can differentiate between them), you can just add all of them to a single generic interface and call that in lieu of the pointer. Alternatively you could use a select type() construct to selectively associate your pointer with the specific procedure for your type to avoid needing to associate with a generic interface.


Here is an example of a wrapper procedure to assign the pointer to a specific procedure based on an argument type

subroutine get_proc_ptr(pp, arg)
  implicit none
  procedure(), pointer, intent(out) :: pp
  class(*), intent(inout) :: arg

  select type(arg)
    type is (real(kind=kind(1d0)))
       pp => norm_r8
    type is (real)
       pp => norm_r
    type is (integer)
       pp => norm_i
    type is (complex)
       pp => norm_c
    class default
       pp => null()
  end select
end subroutine

Which can be made use of like this:

real(kind=kind(1d0)) :: arg_r8
procedure(), pointer :: pNorm => null()

arg_r8 = 4.0123456789d30

call get_proc_ptr(pNorm, arg_r8)
call pNorm(arg_r8)

Here is a complete compilable example:

module proc
  implicit none

  interface norm
    module procedure &
      norm_r8, &
      norm_r,  &
      norm_i,  &
      norm_c
  end interface

contains

  subroutine norm_r8(arg)
    implicit none
    real(kind=kind(1d0)), intent(in) :: arg

    write (*,*) "real8: ", arg
  end subroutine

  subroutine norm_r(arg)
    implicit none
    real, intent(in) :: arg

    write (*,*) "real: ", arg
  end subroutine

  subroutine norm_i(arg)
    implicit none
    integer, intent(in) :: arg

    write (*,*) "integer: ", arg
  end subroutine

  subroutine norm_c(arg)
    implicit none
    complex, intent(in) :: arg

    write (*,*) "complex: ", arg
  end subroutine

  subroutine get_proc_ptr(pp, arg)
    implicit none
    procedure(), pointer, intent(out) :: pp
    class(*), intent(inout) :: arg

    select type(arg)
      type is (real(kind=kind(1d0)))
         pp => norm_r8
      type is (real)
         pp => norm_r
      type is (integer)
         pp => norm_i
      type is (complex)
         pp => norm_c
      class default
         pp => null()
    end select
  end subroutine

end module

program test
  use proc
  implicit none
  real(kind=kind(1d0)) :: arg_r8
  real                 :: arg_r
  integer              :: arg_i
  complex              :: arg_c
  procedure(), pointer :: pNorm => null()

  arg_r8 = 4.0123456789d30
  arg_r = 12.5
  arg_i = 56
  arg_c = (34,3)

  call get_proc_ptr(pNorm, arg_r8)
  call pNorm(arg_r8)

  call get_proc_ptr(pNorm, arg_r)
  call pNorm(arg_r)

  call get_proc_ptr(pNorm, arg_i)
  call pNorm(arg_i)

  call get_proc_ptr(pNorm, arg_c)
  call pNorm(arg_c)

end program

And here is the output of this program:

$ ./testprocptr 
 real8:    4.0123456788999999E+030
 real:    12.5000000    
 integer:           56
 complex:  (  34.0000000    ,  3.00000000    )
Sign up to request clarification or add additional context in comments.

7 Comments

Right, there is even danger with the generic intrinsics like sin or cos. They are not allowed too! One must use the specific name for the right kind or a wrapper.
Thanks! The generic interface unfortunately does not negate my need for a pointer: the idea is that I have a bunch of objective functions (each of them duplicated 3 times with different types), and I want the user to be able to select one of those objective functions independently of the type. So basically the generic interface and the pointer are not used to accomplish the same genericity: the generic interface is there for genericity wrt the variable types, the pointer for genericity wrt the objective function chosen by the user...
@VladimirF Do you have an idea of how to do this with a wrapper? I'm not sure I understand what you mean.
@EtiennePellegrini I have edited with an example of a wrapper. Enjoy.
@casey I've never seen the CLASS(*) declaration used before. Is this some type of generic programming? Instead of going through the 'GENERIC' interface for norm and the get_proc_ptr routine, could I declare a norm subroutine like this: subroutine norm(arg) implicit none CLASS(*) :: arg write (*,*), arg end subroutine and therefore I wouldn't need the overloading of the norm function? Does this have an impact on performance compared to the overload method? I'm new to generic programming and I thought the way to do it in Fortran was overloading.
|
0

If I understand well, you want to achieve two things at one time. First, you want to use polymorphism to let the compiler call the correct routine dependent on whether you have a different type, rank, number of arguments etcetera. Second, you want to use procedure pointers to switch between different procedures that have the same interface.

I tried the same. I did not manage to set a pointer to an interface, but I managed to make an interface with pointers.

If you have a module like this

module some_module

  ! This is the abstract interface for procedure pointers.
  interface
    subroutine shape_1_interface(arg)
      implicit none
      real, intent(in) :: arg
    end subroutine shape_1_interface
    subroutine shape_2_interface(arg)
      implicit none
      integer, intent(in) :: arg
    end subroutine shape_2_interface
  end interface

contains

  subroutine routine_shape_1_implementation_1(arg)
    implicit none
    real, intent(in) :: arg
    write(*,*) "Arg is real",arg
    write(*,*) "Implementation 1"
  end subroutine routine_shape_1_implementation_1

  subroutine routine_shape_2_implementation_1(arg)
    implicit none
    integer, intent(in) :: arg
    write(*,*) "Arg is int",arg
    write(*,*) "Implementation 1"
  end subroutine routine_shape_2_implementation_1

  subroutine routine_shape_1_implementation_2(arg)
    implicit none
    real, intent(in) :: arg
    write(*,*) "Arg is real",arg
    write(*,*) "Implementation 2"
  end subroutine routine_shape_1_implementation_2

  subroutine routine_shape_2_implementation_2(arg)
    implicit none
    integer, intent(in) :: arg
    write(*,*) "Arg is int",arg
    write(*,*) "Implementation 2"
  end subroutine routine_shape_2_implementation_2

  subroutine routine_shape_1_implementation_3(arg)
    implicit none
    real, intent(in) :: arg
    write(*,*) "Arg is real",arg
    write(*,*) "Implementation 3"
  end subroutine routine_shape_1_implementation_3

  subroutine routine_shape_2_implementation_3(arg)
    implicit none
    integer, intent(in) :: arg
    write(*,*) "Arg is int",arg
    write(*,*) "Implementation 3"
  end subroutine routine_shape_2_implementation_3

end module some_module

then you can do in your main program:

program main

  use some_module

  implicit none

  procedure(shape_1_interface), pointer :: routine_shape_1
  procedure(shape_2_interface), pointer :: routine_shape_2

  interface routine
    procedure routine_shape_1
    procedure routine_shape_2
  end interface routine

  routine_shape_1 => routine_shape_1_implementation_1
  routine_shape_2 => routine_shape_2_implementation_1

  call routine(4)

  routine_shape_1 => routine_shape_1_implementation_2
  routine_shape_2 => routine_shape_2_implementation_2

  call routine(4.0)

end program main

It is a pity that when you want to set the pointers to a different implementation, you have to do that for all shapes, but the good thing is that you can just call 'routine' and you automatically get the desired function.

This is the output:

Arg is int           4
Implementation 1
Arg is real   4.00000000000000     
Implementation 2

Comments

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.