13
$\begingroup$

Here is a typical usage of function pointers in C. I would like to do something similar in Fortran. I have some ideas, but I would like to know if there is some canonical way to do so.

The function pointers and contexts passed in by the user are stored, then called later.

typedef PetscErrorCode (*TSIFunction)(TS,PetscReal,Vec,Vec,Vec,void*);
PetscErrorCode TSSetIFunction(TS ts,Vec res,TSIFunction f,void *ctx);

The user's function is called back using their context at various later times.

In PETSc, they also make heavy use of string -> function pointer tables. Everything is a plugin, so the user can register their own implementations and they are first-class.

#define PCGAMG "gamg"
  PCRegisterDynamic(PCGAMG         ,path,"PCCreate_GAMG",PCCreate_GAMG);

This registers the creation routine in an "FList", then PCSetFromOptions() offers the ability to choose this method versus any of the other choices. If the system supports dynamic loading, you can skip the compile-time dependency on the PCCreate_GAMG symbol and just pass NULL, then the symbol will be looked up in the shared library at run time.

Note that this one step beyond a "factory", it's an inversion of control device similar to what Martin Fowler calls "service locator".

Note: this came up in my private correspondence with Jed Brown, where he asked this question to me. I decided to outsource it and see what answers people can come up with.

$\endgroup$

2 Answers 2

5
$\begingroup$

There's no need to use transfer to emulate void * in a modern Fortran code. Instead, just use the ISO_C_BINDING intrinsic module, which is supported by all mainstream Fortran compilers. This module makes it very simple to interface between Fortran and C, with some very minor caveats. One can use the C_LOC and C_FUNLOC functions to get C pointers to Fortran data and procedures, respectively.

With regard to the PETSC example above, I assume the context is typically a pointer to a user-defined structure, which is equivalent to a derived data type in Fortran. That shouldn't be a problem to deal with using C_LOC. The opaque TSIFunction handle is also very simple to deal with: just use the ISO_C_BINDING data type c_ptr, which is equivalent to void * in C. A library written in Fortran can use c_ptr if it needs to work around modern Fortran's strict type checking.

$\endgroup$
1
8
$\begingroup$

There is a lot of what I'm guessing is PETSc-specific language in your question (with which I am unfamiliar), so there might be a wrinkle here I don't quite understand, but maybe this will still be useful to get you started.

Basically, you have to define the interface for the procedure, and then you can pass a pointer to a function that follows this interface. The following code shows an example. First, there is a module that defines the interface and shows a quick example of a chunk of code that would execute the routine provided by the user that follows that interface. Next is a program that shows how the user would use this module and define the function to be executed.

MODULE xmod

  ABSTRACT INTERFACE
  FUNCTION function_template(n,x) RESULT(y)
      INTEGER, INTENT(in) :: n
      REAL, INTENT(in) :: x(n)
      REAL :: y
  END FUNCTION function_template
  END INTERFACE

CONTAINS

  SUBROUTINE execute_function(n,x,func,y)
    INTEGER, INTENT(in) :: n
    REAL, INTENT(in) :: x(n)
    PROCEDURE(function_template), POINTER :: func
    REAL, INTENT(out) :: y
    y = func(n,x)
  END SUBROUTINE execute_function

END MODULE xmod


PROGRAM xprog

  USE xmod

  REAL :: x(4), y
  PROCEDURE(function_template), POINTER :: func

  x = [1.0, 2.0, 3.0, 4.0]
  func => summation

  CALL execute_function(4,x,func,y)

  PRINT*, y  ! should give 10.0

CONTAINS

  FUNCTION summation(n,x) RESULT(y)
    INTEGER, INTENT(in) :: n
    REAL, INTENT(in) :: x(n)
    REAL :: y
    y = SUM(x)
  END FUNCTION summation

END PROGRAM xprog
$\endgroup$
5
  • $\begingroup$ Thanks for the answer. The PETSc example above also stores the function pointer in some internal data structure, but I think that it is quite trivial to save PROCEDURE(function_template), POINTER :: func internally. $\endgroup$ Commented Dec 7, 2011 at 21:43
  • $\begingroup$ Note that the pointer is an opaque object rather than the address of that code, so AFAIK there can't be interoperability with C. In PETSc, we have to maintain tables of function pointers for C wrappers for these things. $\endgroup$ Commented Dec 7, 2011 at 23:33
  • $\begingroup$ The PETSc example stores both the function pointer and the context (private user data that is passed back when the function is called). The context is really crucial, otherwise the user ends up doing horrible things like referencing globals. Since there is no equivalent to void*, the user ends up having to write interfaces for library functions themselves. If you implement the library in C, this is enough, but if you implement in Fortran, you have to ensure that the compiler never sees the library's "dummy" INTERFACE at the same time as the user's INTERFACE. $\endgroup$
    – Jed Brown
    Commented Dec 8, 2011 at 5:14
  • 2
    $\begingroup$ The equivalent of void* in Fortran is a transfer method. See here for an example of usage. The other 3 approaches besides the transfer method are "work arrays", "specific derived type instead of void *" and use module variables local to the module. $\endgroup$ Commented Dec 8, 2011 at 6:49
  • $\begingroup$ It's a shame the user has to muck around with transfer to a nonsense type (character (len=1), allocatable) just to call the function. $\endgroup$
    – Jed Brown
    Commented Dec 9, 2011 at 8:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.