2016-11-09 15 views
4

추상 유형으로 지원되는 다형성을 사용하여 수치 최적화를위한 객체 지향 포트란 코드를 개발 중입니다. 좋은 TDD 연습이므로 추상화 유형 class(generic_optimizer)에 모든 최적화 테스트를 작성하려고합니다. 그러면 각 인스턴스화 된 클래스에 의해 실행되어야합니다 (예 : type(newton_raphson)).지연 함수 및 non_overridable 키워드로 세그먼트 오류

모든 최적화 테스트에는 call my_problem%solve(...)에 대한 호출이 있으며, 이는 추상 유형으로 deferred으로 정의되며 물론 각 파생 유형에서 다른 구현을 특징으로합니다.

문제는 다음 몇 가지 시행 착오 후

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로 이연 함수를 정의하면, 내가 같은 세그먼트 오류를 ​​얻을 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 
+0

빠른보기에서 'non_overridable' 속성이 금지되어 있고 나중에 무시하지 않는 이유가 분명하지 않습니다. 하지만 문제를 재현하기 위해 컴파일러에 액세스 할 수는 없습니다. – francescalus

+0

나에게 컴파일러 버그가있는 것 같습니다. – IanH

+0

Deferreds와 어떤 관련이 있습니까? –

답변

0

문제를 좁히려면, 나는 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 
,691,363,210

은 또한 (실제로 루틴이라고 인쇄하는) 서브 루틴과 기능을 대체하려고 모든 다른 부품

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 

이 동일하게 유지. 나는 위의 코드에서 non_overridable를 제거하면 ifort- {14,16}와 오라클은 포트란 12.5

Type1_square: 
square( 2.000000 ) = -100.0000  
Type2_square: 
square( 2.000000 ) = 4.000000 

을 제공하면서 그런 다음, gfortran-6

Type1_square: 
square( 2.00000000 ) = -100.000000  
Type1_square: 
square( 2.00000000 ) = -100.000000 

을 제공 gfortran은 다른 같은 결과를 제공 컴파일러. 따라서, gfortran + non_overridable (위의 코드가 표준에 부합하는 경우)에 대한 특정 문제 일 수 있습니다 ...

(OP가 세분화 오류를 얻은 이유는 부모 유형에서 deferred 절차에 액세스 한 것일 수 있습니다. generic_type) 널 포인터를 갖는이 경우 이야기가 일관되고) 우리가 abstract으로 타입 1을 선언 할 때


편집

gfortran의 같은 예외적 인 동작이 발생합니다..우리가

type, abstract :: Type1 ! now an abstract type (cannot be instantiated) 
    contains 
     procedure :: test 
     procedure :: square => Type1_square 
    endtype 

같은 타입 1의 정의와

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

    call t2 % test(2.0) 
end program 

등의 주요 프로그램을 변경하는 경우 우리가 더 deferred를 할 타입 1에 square()을하면 특히, 우리가

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

를 얻을 수 (즉, 구현이 주어지지 않았다.) 그래서 코드는 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는 call t2 % test(2.0)으로 4.0을 제공하지만 gfortran-6은 세그먼트 오류를 ​​발생시킵니다. 바인딩 이름 타입 1에서 square()이 (구현을 가지지없는) 널 (null)와 (아마도 gfortran에 의해 잘못이라고 것처럼 우리가

$ 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. 

를 얻을 것 같다 바늘). 더 중요한 것은 Type2의 정의에서 non_overridable을 삭제하면 gfortran도 4.0을 제공한다는 것입니다 (세그멘테이션 오류 없음).

+0

매우 흥미로운 @roygvib, 감사합니다! gfortran은 non_overridable이 확장 된 유형에서 지정되는 경우에도 부모 루틴이 호출된다고 가정합니다. - "non_overridable"속성이 나타나는 경우, 타입 바인딩 된 프로시져는 타입 확장 중에 오버라이드 될 수 없습니다. -'non_overridable'은'deferred'와 호환되지 않습니다. 왜냐하면 그것은 타입 - 바운드 프로 시저를 오버라이드해야하기 때문입니다; - 상속 된 프로 시저가'non_overridable' 속성을 가지고 있다면 타입 바인딩 프로 시저를 오버라이드 할 수 없습니다. –

+0

안녕하세요, 귀하의 원래 코드를 다시 고려했지만 여전히 코드가 올바른지 내게 보인다 ... 내 이해는 Type2의 형식 바인딩 된 프로 시저에 'non_overridable'을 첨부하는지 여부에 상관없이 해당 구현 (즉 Type2_square)는 Type2의 인스턴스에서 호출되어야합니다. (내 이해는'non_overridable'은 Type2의 자식 타입 (Type3)에 의해 square()가 덮어 쓰여질 수 없으므로 Type2 자신의 행위에 아무런 영향을 미치지 않는다는 것입니다. – roygvib

+0

"non_overridable"문장은 호환되지 않습니다 "Modern Fortran Explained"(나는 Kindle 버전을 샀다.)의 Chap.14.6.1에''deferred ''라고 적혀 있는데, 이것은'procedure (...), deferred, non_overridable :: square '즉, 두 키워드를 동시에 지정할 수는 없습니다. 반면 Type1에는'procedure (...), deferred :: square', Type2에는'procedure, non_overridable :: square => Type2_square'를 사용하는 것이 좋습니다. – roygvib