gpt4 book ai didi

oop - 带有延迟函数和 non_overridable 关键字的段错误

转载 作者:行者123 更新时间:2023-12-03 14:50:20 25 4
gpt4 key购买 nike

我正在开发一个面向对象的 Fortran 代码,用于通过抽象类型支持的多态性进行数值优化。因为这是一个很好的 TDD 实践,所以我尝试以抽象类型 class(generic_optimizer) 编写所有优化测试。 ,然后应该由每个实例化的类运行,例如,由 type(newton_raphson) .

所有优化测试都调用 call my_problem%solve(...) , 定义为 deferred在抽象类型中,当然在每个派生类型中都有不同的实现。

问题是:如果在每个非抽象类中我将延迟函数定义为 non_overridable ,我得到段错误,例如:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()

(gdb) where
#0 0x0000000000000000 in ?? ()
#1 0x0000000000913efe in __newton_raphson_MOD_nr_solve ()
#2 0x00000000008cfafa in MAIN__ ()
#3 0x00000000008cfb2b in main ()
#4 0x0000003a3c81ed5d in __libc_start_main () from /lib64/libc.so.6
#5 0x00000000004048f9 in _start ()

经过反复试验,我注意到如果删除 non_overridable 可以避免该错误。宣言。在这种情况下,这不是问题,但我想强制执行这一点,因为此代码不太可能存在两个级别的多态性。相反,我是否违反了标准中的任何要求?

这是重现错误的示例代码。我一直在用 gfortran 5.3.0 和 6.1.0 对其进行测试。
module generic_type_module
implicit none
private

type, abstract, public :: generic_type
real(8) :: some_data
contains
procedure (sqrt_interface), deferred :: square_root
procedure, non_overridable :: sqrt_test
end type generic_type

abstract interface
real(8) function sqrt_interface(this,x) result(sqrtx)
import generic_type
class(generic_type), intent(in) :: this
real(8), intent(in) :: x
end function sqrt_interface
end interface

contains

subroutine sqrt_test(this,x)
class(generic_type), intent(in) :: this
real(8), intent(in) :: x
print *, 'sqrt(',x,') = ',this%square_root(x)
end subroutine sqrt_test

end module generic_type_module

module actual_types_module
use generic_type_module
implicit none
private

type, public, extends(generic_type) :: crashing
real(8) :: other_data
contains
procedure, non_overridable :: square_root => crashing_square_root
end type crashing
type, public, extends(generic_type) :: working
real(8) :: other_data
contains
procedure :: square_root => working_square_root
end type working

contains

real(8) function crashing_square_root(this,x) result(sqrtx)
class(crashing), intent(in) :: this
real(8), intent(in) :: x
sqrtx = sqrt(x)
end function crashing_square_root
real(8) function working_square_root(this,x) result(sqrtx)
class(working), intent(in) :: this
real(8), intent(in) :: x
sqrtx = sqrt(x)
end function working_square_root

end module actual_types_module

program deferred_test
use actual_types_module
implicit none
type(crashing) :: crashes
type(working) :: works

call works%sqrt_test(2.0_8)
call crashes%sqrt_test(2.0_8)

end program

最佳答案

为了缩小问题范围,我从 OP 的代码中删除了抽象属性和数据成员,这样

module types
implicit none

type :: Type1
contains
procedure :: test
procedure :: square => Type1_square
endtype

type, extends(Type1) :: Type2
contains
procedure, non_overridable :: square => Type2_square
endtype

contains

subroutine test( this, x )
class(Type1) :: this
real :: x
print *, "square(", x, ") = ",this % square( x )
end subroutine

function Type1_square( this, x ) result( y )
class(Type1) :: this
real :: x, y
y = -100 ! dummy
end function

function Type2_square( this, x ) result( y )
class(Type2) :: this
real :: x, y
y = x**2
end function

end module

program main
use types
implicit none
type(Type1) :: t1
type(Type2) :: t2

call t1 % test( 2.0 )
call t2 % test( 2.0 )
end program

使用此代码,gfortran-6 给出
square(   2.00000000     ) =   -100.000000
square( 2.00000000 ) = -100.000000

而 ifort-{14,16} 和 Oracle fortran 12.5 给出
square(   2.000000     ) =   -100.0000    
square( 2.000000 ) = 4.000000

我还尝试用子例程替换函数(以打印实际调用了哪些例程):
    subroutine test( this, x )
class(Type1) :: this
real :: x, y
call this % square( x, y )
print *, "square(", x, ") = ", y
end subroutine

subroutine Type1_square( this, x, y )
class(Type1) :: this
real :: x, y
print *, "Type1_square:"
y = -100 ! dummy
end subroutine

subroutine Type2_square( this, x, y )
class(Type2) :: this
real :: x, y
print *, "Type2_square:"
y = x**2
end subroutine

所有其他部分保持不变。然后,gfortran-6 给出
Type1_square:
square( 2.00000000 ) = -100.000000
Type1_square:
square( 2.00000000 ) = -100.000000

而 ifort-{14,16} 和 Oracle fortran 12.5 给出
Type1_square:
square( 2.000000 ) = -100.0000
Type2_square:
square( 2.000000 ) = 4.000000

如果我删除 non_overridable从上面的代码中,gfortran 给出了与其他编译器相同的结果。因此,这可能是 gfortran + non_overridable 的特定问题(如果上面的代码符合标准)......

(OP 出现段错误的原因可能是 gfortran 访问了具有空指针的父类型( deferred )中的 generic_type 过程;如果是这种情况,故事就变得一致了。)

编辑

当我们将 Type1 声明为 abstract 时,也会发生 gfortran 的相同异常行为。 .具体来说,如果我们将 Type1 的定义更改为
    type, abstract :: Type1    ! now an abstract type (cannot be instantiated)
contains
procedure :: test
procedure :: square => Type1_square
endtype

和主程序为
program main
use types
implicit none
type(Type2) :: t2

call t2 % test( 2.0 )
end program

我们得到
ifort-16    : square(   2.000000     ) =    4.000000    
oracle-12.5 : square( 2.0 ) = 4.0
gfortran-6 : square( 2.00000000 ) = -100.000000

如果我们进一步制作 square()在 Type1 中为 deferred (即,没有给出实现),因此使代码几乎等同于 OP 的情况,
type, abstract :: Type1  ! now an abstract type (cannot be instantiated)
contains
procedure :: test
procedure(Type1_square), deferred :: square ! has no implementation yet
endtype

abstract interface
function Type1_square( this, x ) result( y )
import
class(Type1) :: this
real :: x, y
end function
end interface

那么 ifort-16 和 Oracle-12.5 给出 4.0 和 call t2 % test( 2.0 ) ,而 gfortran-6 导致段错误。事实上,如果我们编译为
$ gfortran -fsanitize=address test.f90   # on Linux x86_64

我们得到
ASAN:SIGSEGV    (<-- or "ASAN:DEADLYSIGNAL" on OSX 10.9)
=================================================================
==22045==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
(pc 0x000000000000 bp 0x7fff1d23ecd0 sp 0x7fff1d23eac8 T0)
==22045==Hint: pc points to the zero page.

所以总的来说,似乎绑定(bind)名称 square()在 Type1 中(没有实现)被 gfortran 错误地调用(可能带有空指针)。更重要的是,如果我们放弃 non_overridable从Type2的定义来看,gfortran也给出了4.0(没有段错误)。

关于oop - 带有延迟函数和 non_overridable 关键字的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40510423/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com