首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >派生类型之间传递的Nopass过程指针导致分割错误

派生类型之间传递的Nopass过程指针导致分割错误
EN

Stack Overflow用户
提问于 2022-04-01 16:08:24
回答 2查看 111关注 0票数 3

我想在现代Fortran的两个类之间传递一个过程指针。这个过程指针应该

  1. 从第二个对象
  2. 中调用,以访问第一个对象的组件,而不使用它作为虚拟参数。

这里有一个明确的例子,想象一下对ODE解决程序执行一个面向对象的包装:

代码语言:javascript
复制
module test_funptr
    implicit none
    public

    type, public :: ode_solver
        integer :: NEQ = 0
        procedure(ode_api), pointer, nopass :: f => null()
    contains
        procedure :: run
    end type ode_solver

    type, public :: ode_problem
        integer :: NEQ = 10
        procedure(ode_api), pointer, nopass :: yprime => null()
    contains
        procedure :: init
    end type ode_problem

    abstract interface
        subroutine ode_api(NEQ,YDOT)
            integer, intent(in) :: NEQ
            real(8), intent(inout) :: YDOT(NEQ)
        end subroutine ode_api
    end interface
contains
    ! Initialize problem variables
    subroutine init(this,NEQ)
        class(ode_problem), intent(inout) :: this
        integer, intent(in) :: NEQ

        ! Associate function pointer
        this%yprime => problem_api
    contains
        ! nopass ODE solver API
        subroutine problem_api(NEQ,YDOT)
            integer, intent(in) :: NEQ
            real(8), intent(inout) :: YDOT(NEQ)

            integer :: i

            print *, 'entered problem API with NEQ=',NEQ
            forall(i=1:NEQ) YDOT(i) = real(i,8)
        end subroutine
    end subroutine init

    subroutine run(this)
        class(ode_solver), intent(inout) :: this

        real(8) :: ydot(this%neq)

        ydot = 0.0

        print *, 'enter solver run with NEQ=',this%NEQ
        print *, 'is function associated? ',associated(this%f)

        call this%f(this%neq,ydot)
    end subroutine run
end module test_funptr

program test
    use test_funptr

    type(ode_solver) :: solver
    type(ode_problem) :: prob

    call prob%init(10)

    ! Associate ode solver
    solver%neq = prob%NEQ
    solver%f => prob%yprime

    call solver%run()
end program test

此程序返回gfortran-10:

代码语言:javascript
复制
 enter solver run with NEQ=          10
 is function associated?  T

Program received signal SIGILL: Illegal instruction.

这个过程似乎有适当的关联,但不能调用它。我是做了什么错误的传递程序指针,还是我做了一些不符合标准的事情?我担心contained子程序可能超出了范围,但是如果是的话,我如何实现这种行为呢?

当然,棘手的部分是函数应该访问来自其他变量实例的数据。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-04-01 20:08:31

正如所指出的,内部(contained)过程不是要走的路,因为它们不能成为过程指针的目标。希望编译器能够捕捉到这一点。

我想出了一种很好的方法来实现在两个类之间传递一个接口过程的目的:

类1需要调用该函数:它必须包含指向类2

  • The 2
  1. class函数的指针,作为一个内部过程(这样,它永远不会超出范围)
  2. 该类必须包含一个指向实例化对象的(多态)指针,类2
    1. class 2包含实际实现,它应该实例化包含相同接口函数的抽象类型,但以派生类型作为虚拟参数

在这里,我提供了一个实用的实现:

代码语言:javascript
复制
module odes 
    implicit none

    type, abstract, public :: ode_problem
           integer :: NEQ
       contains
           procedure(ode_api), deferred :: fun
    end type ode_problem

    type, public :: ode_solver
         integer :: NEQ
         class(ode_problem), pointer :: problem => null()
         contains
             procedure :: init
             procedure :: run
    end type ode_solver

    abstract interface
       subroutine ode_api(this,YDOT)
           import ode_problem
           class(ode_problem), intent(inout) :: this
           real(8), intent(out) :: YDOT(this%NEQ)
       end subroutine ode_api
    end interface

    contains

    ! Associate problem to ODE solver
    subroutine init(this,my_problem)
        class(ode_solver), intent(inout) :: this
        class(ode_problem), intent(in), target :: my_problem

        this%neq = my_problem%NEQ
        this%problem => my_problem

    end subroutine init

    ! call the nopass f77 interface function
    subroutine run(this)
       class(ode_solver), intent(inout) :: this
       real(8) :: YDOT(this%NEQ)
       integer :: i 

       if (.not.associated(this%problem)) stop 'solver not associated to a problem'

       ! This will be in general passed to another function as an argument 
       call ode_f77_api(this%NEQ,YDOT)

       contains

         subroutine ode_f77_api(NEQ,YDOT)
             integer, intent(in) :: NEQ
             real(8), intent(out) :: YDOT(NEQ)

             ! This is just a nopass interface to this problem's function that can
             ! access internal storage
             call this%problem%fun(YDOT)
         end subroutine ode_f77_api

    end subroutine run    

end module odes

! Provide an actual implementation
module my_ode_problem
   use odes
   implicit none

        type, public, extends(ode_problem) :: exp_kinetics
            real(8) :: k = -0.5d0
            contains
               procedure :: fun => exp_fun
        end type exp_kinetics

   contains

        subroutine exp_fun(this,YDOT) 
            class(exp_kinetics), intent(inout) :: this
            real(8), intent(out) :: YDOT(this%NEQ)
            integer :: i

            forall(I=1:this%NEQ) YDOT(i) = this%k*real(i,8)
            print 1, this%NEQ,(i,YDOT(i),i=1,this%NEQ)

            1 format('test fun! N=',i0,': ',*(/,10x,' ydot(',i0,')=',f5.2,:))

        end subroutine exp_fun

end module my_ode_problem

program test_fun_nopass
        use odes
        use my_ode_problem
        implicit none

        type(exp_kinetics) :: prob
        type(ode_solver) :: ode

        prob%NEQ = 10
        call ode%init(prob)

        call ode%run()

        stop 'success!'
end program test_fun_nopass          

此程序返回:

代码语言:javascript
复制
test fun! N=10: 
           ydot(1)=-0.50
           ydot(2)=-1.00
           ydot(3)=-1.50
           ydot(4)=-2.00
           ydot(5)=-2.50
           ydot(6)=-3.00
           ydot(7)=-3.50
           ydot(8)=-4.00
           ydot(9)=-4.50
           ydot(10)=-5.00
STOP success!
票数 3
EN

Stack Overflow用户

发布于 2022-04-01 18:05:03

在宿主过程超出作用域后,调用指向内部过程的过程指针是非法的。

Fortran 2015 N2123草案在附注15.17中提到了这一点

附注15.17

在宿主实例完成执行后,不能使用Fortran或C中的过程指针调用内部过程,因为指针随后是未定义的。但是,当主机实例处于活动状态时,如果内部过程作为实际参数传递,或者是过程指针的目标,则可以从宿主子程序之外调用它。

..。下面是一个例子

经常使用蹦床来执行内部程序。也就是说,放置在堆栈上的一段可执行代码,它允许访问本地范围并调用过程本身。然后指针是指向蹦床的指针。一旦主机函数超出作用域,指向堆栈的指针就无效。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71709880

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档