如何在科学程序中使用Fortran中的函数指针


11

这是C中函数指针的典型用法。我想在Fortran中做类似的事情。我有一些想法,但是我想知道是否有一些规范的方法可以做到。

用户传递的函数指针和上下文将被存储,然后在以后调用。

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

在以后的不同时间使用他们的上下文来回调用户的功能。

在PETSc中,它们也大量使用字符串->函数指针表。一切都是插件,因此用户可以注册自己的实现,并且是一流的。

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

这会将创建例程注册在“ FList”中,然后PCSetFromOptions()可以选择此方法,而不是其他任何选择。如果系统支持动态加载,则可以跳过对PCCreate_GAMG符号的编译时依赖性,而只传递NULL,则将在运行时在共享库中查找该符号。

请注意,这一步骤超出了“工厂”的范围,它是一种类似于马丁·福勒(Martin Fowler)称之为“服务定位器”的控制设备的倒置。

注意:这是在我与杰德·布朗的私人通信中提出的,他在那儿向我提出了这个问题。我决定将其外包,看看人们能提出什么答案。

Answers:


5

无需使用传输void *在现代Fortran代码中进行仿真。相反,只需使用所有主流Fortran编译器都支持的ISO_C_BINDING内部模块。此模块使Fortran与C之间的接口非常简单,但有一些小小的警告。可以使用C_LOCC_FUNLOC函数分别获取指向Fortran数据和过程的C指针。

关于上面的PETSC示例,我假定上下文通常是指向用户定义的结构的指针,该结构等效于Fortran中的派生数据类型。使用处理应该不成问题C_LOC。不透明的TSIFunction句柄也非常简单:只需使用ISO_C_BINDING数据类型c_ptr,就相当于void *C语言。c_ptr如果需要解决现代Fortran的严格类型检查问题,可以使用用Fortran编写的库。


Brian,是的,与此同时,Jed和我已经找到了许多有关回调的解决方案,请参见此处:fortran90.org/src/best-practices.html#type-casting-in-callbacks,类型(c_ptr)是节号V。
OndřejČertík2012年

9

我猜想您的问题中有很多是PETSc专用语言(我不熟悉),所以我可能不太理解这里有一些皱纹,但是也许这对您有用开始。

基本上,您必须为该过程定义接口,然后可以将指针传递给该接口之后的函数。以下代码显示了一个示例。首先,有一个模块定义了接口,并显示了一段代码的快速示例,该代码将执行用户遵循该接口提供的例程。接下来是一个程序,该程序显示用户如何使用此模块并定义要执行的功能。

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

感谢你的回答。上面的PETSc示例还将函数指针存储在一些内部数据结构中,但是我认为在内部进行保存非常简单PROCEDURE(function_template), POINTER :: func
OndřejČertík”,2011年

请注意,指针是一个不透明的对象,而不是该代码的地址,因此AFAIK不能与C互操作。在PETSc中,对于这些事情,我们必须为C包装器维护函数指针表。
Matt Knepley 2011年

PETSc示例存储函数指针和上下文(调用函数时传回的私有用户数据)。上下文确实很关键,否则用户最终会做可怕的事情,例如引用全局变量。由于没有等效于void*,用户最终不得不自己为库函数编写接口。如果用C实现库就足够了,但是如果用Fortran实现,就必须确保编译器永远不会与用户的INTERFACE同时看到库的“虚拟”接口。
杰德·布朗

2
void*Fortran中的等效是一种transfer方法。有关用法的示例,请参见此处。除此方法外,其他3种transfer方法是“工作数组”,“特定派生类型而不是void *”,并使用模块本地的模块变量。
2011年

用户只能调用transfer一个废话类型(character (len=1), allocatable),这真是太可惜了。
杰德·布朗
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.