Procedures as Arguments

Procedures as Arguments

Portrait de TommyCee

It's been quite a while since I posted.  I am still working to upgrade legacy (F77) code to F90.  Recently, I have stumbled onto situations in which procedures are being passed as arguments in the call.  I'm not sure how common this is; I had never seen it and discovered it quite accidentally.  This (I discovered) exlained the EXTERNAL statements in the specificaon part of certain routines.  Upon researching this, I believe I discovered a way to "convert" this structure to F90; it involves the use of the INTERFACE block.  I prepared a skeleton for illustration:

1) Can anyone please confirm that this template is appropriate?  (I realize I've omitted much detail; I'm trying to get the concept for now, with an eye toward parallelism.)

2) In the call to s.D, procedures B & C - which have their own arguments - are (of course) referenced as arguments.  How can procedures B & C be executed without arguments?

3) If the F90 structure is appropriate, will it work as well if all the procedures (s.A, s.B, S.D & f.C) are in the CONTAINS part of a Module?

I hope I have presented this clearly and appreciate any helpful feedback.  ( I also hope my pasted table appears as well as it does in the edit window; I've never tried this before.)

12 posts / 0 nouveau(x)
Dernière contribution
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.
Portrait de TommyCee

I see my table did not come through.  Rats!  I will try to attach it here.

Fichiers joints: 

Fichier attachéTaille
Téléchargement illustration-table.jpg113.6 Ko
Portrait de mecej4

The EXTERNAL attribute on a symbol tells the compiler not to treat the symbol as a local variable. The compiler, in turn, has to insert information in the object file telling the linker to resolve this symbol at link time (or even later, at run-time). If the external symbol is for a subroutine, it is typeless. If the external symbol is a function, its type has to be specified as well (explicitly or implicitly).

Functions as arguments are essential when working with nonlinear problems. A simple example is to solve f(x) = 0 for x, where f(x) is a nonlinear function. Such functions, especially when they cannot be built up with scalar, vector and matrix operations, need to be coded as "custom" functions.

Specifying the interface for subprograms that are arguments by writing interface blocks is useful, but a better solution is to write abstract interfaces to those subprograms in a module, and to USE the module wherever a declaration of the type and attributes of the subroutine/function is needed. For example:

MODULE FUNINTF

abstract interface

    real function fnc(x)

    real, intent(in) :: x

   end function fnc

end interface

END MODULE

...

USE FUNINTF

   procedure(fnc) :: fncA, fncB

...

declares fncA and fncB as two instances of functions with the same interface as that in the abstract interface; that is, they are both functions of type real, and take a single argument of type real with intent(IN). The compiler can now check for the number, types and INTENTs of the arguments in the invocations of these functions in the source code.

Portrait de Steve Lionel (Intel)

mecej4 answered your question well. I just wanted to mention that Fortran 2008 adds the ability to pass an internal procedure, one that is CONTAINed inside another procedure or main program, as an actual argument. (Passing a module procedure has been fine since Fortran 90.) Intel Fortran has supported this for many years, but some other Fortran compilers don't support it yet. For some amusing additional reading on this topic, see Doctor Fortran in "Think, Thank, Thunk"

Steve
Portrait de TommyCee

Think, Thank,Thunk was interesting.  I wish the Dr. Fortran articles had a print dialog.

Thanks for your feedback Mecej & Steve.  I had not heard of the abstract interface. I'll have to research it more.  I gather the point is one of ecomomizing:  one can use a single abstract interface tucked inside a custom module, which can then be simply referenced in the various subprograms that need it (like my subroutine A) via an appropriate use statement at the top of those subprograms.  (This would keep you from repeating the interface among several subprograms.)  Is it fair to say that Module FUNINTF can be part of (inside of) another existing Module?

I'd also like to get some thoughts on my Question 2.  In my example, how do s.B & f.C know how to behave when no argumenets are passed to them via the call?  It's kinda academic; maybe it's just me but this seems like magic ...

Portrait de Steve Lionel (Intel)

You should be able to just use your browser's print feature. I find it works pretty well in recent FF and IE browsers.

For your question 2, you are required to call B and C with the arguments they expect. If D can see the abstract interface as mecej4 described, then the compiler will enforce that. If B and C have arguments that require the caller see an explicit interface, you'll need to provide that. The arguments you show in the JPG don't need it.

Steve
Portrait de TommyCee

Hmmm... Maybe one of us is not understanding.  Forget the interfaces for a minute and just look at the simple F77 side of my table.  There are places in other subroutines in which s.B is called directly (in a very usual and ordinary way).  The call statement would look something like this:

Call B ( argX, argY, argZ)

s.B knows what to return becasuse it's fed explicit arguments at its point of call.

In the example case (a call to s.D), however, the only arguments are procedures whose arguments are missing!  So my question remains:  In the example case, very much unlike the case I presented just above, how do s.B and f.C know what to do? They're invoked but passed no arguments?  Capiche?

Sorry to be so thick.

Portrait de TommyCee

Maybe what we're saying is that, in the call to s.D, it is assumed that the arguments associated with s.B & f.C are invoked implicitely and that (hopefully) any incoming variables expected by s.B & f.C are set by the time s.D is called.  Presuming that's the case, I guess I would further presume that any outputs from s.B & f.C are made available to s.D (else why have them be part of the call to s.D in the first place?).

Do I have any of that right?

Portrait de mecej4

I think that you are doing too much guessing as to what happens when you use functions and subroutines as arguments. Just as we can have numeric, logical and character type variables, we can also have variables that stand for procedures. With numeric variables, we can evaluate arithmetical expressions containing them. With character variables, we can extract substrings, concatenate them, change case, etc. With procedure variables, we can invoke them either separately (CALL a subroutine) or as part of an expression (a function reference in the expression), or hand them off to other procedures as arguments that can, in turn, invoke their procedure arguments.

Here is a silly example. I have a subroutine which takes two arguments: (i) a function, and (ii) a variable to receive the value of the function. The argument of the function is NOT part of the subroutine argument list, but is set to π in the subroutine before evaluating the function. The main program calls the subroutine twice, (i) with a function that returns the square of its argument, and (ii) a function that returns the cube of its argument.

The subroutine acts as a mere forwarding agent, and does not know anything about what is done inside the functions.

Note that there is considerable flexibility as to where to do each variable assignment and each evaluation.

module funint

abstract interface

   real function f1var(x)

     real, intent(in) :: x

   end function f1var

end interface

end module funint

!

program showGenIntf

use funint

real :: yvala,yvalb

call eval(f1a,yvala)

call eval(f1b,yvalb)

write(*,*)' For x = Pi, f1a(x) = ',yvala,',  f1b(x) = ',yvalb

!

contains

!

   real function f1a(x)   ! returns square of argument

   real, intent(in) :: x

   f1a=x*x

   return

   end function f1a

!

   real function f1b(x)   ! returns cube of argument

   real, intent(in) :: x

   f1b=x*x*x

   return

   end function f1b

!

   subroutine eval(fun,fval)   ! sets fval = fun(Pi)

   procedure(f1var) :: fun

   real, intent(out) :: fval

   real :: x=acos(-1.0)

   fval=fun(x)

   return

   end subroutine eval

!

end program showGenIntf 

Portrait de TommyCee

I thank you for the time you took to present the example template, mecej4.  It's not exactly like my situation.  In your case, though you do it in a very sopphisticated way, it's easy to see how the functions (F1A or F1B) get their seed (x-value):  it's supplied in s.Eval.  In my case, it's much more cryptic.  The procedures (subprograms) are invoked in the call to s.D without it being obvous how they get their seeds, or what happens to their respective outputs at runtime.  I'll let that rest for now.  I was intrigued by your new (to me) structure that made use of the abstract interface.  I wanted to study it more but unfortunately was unable to compile it in F90 w/o many errors.  I wonder if it's formatted for F03?

In any case, I committed it to paper, annotated it w/ color hilighters, and was able to study its behavior.  It's quite interesting (not silly at all) and gives me some ideas.

Portrait de Steve Lionel (Intel)

ABSTRACT INTERFACE is a Fortran 2003 feature that has been supported in Intel Fortran for a while now. Whose compiler are you using?

Steve
Portrait de TommyCee

Duh!  For reasons I won't get into here, I was using our old friend CFV (v6.6x).  I loaded the code into an IVF project and it compiled/linked/ran fine.  I now understand that, to play w/ the Abstract Interface, I'll have to use IVF.  In tracing it, it behaved exactly as I suspected looking at the code on paper.  I think the AI may prove to be a powerful feature ...

Connectez-vous pour laisser un commentaire.