2016-11-09 5 views
4

Я разрабатываю объектно-ориентированный код Fortran для численной оптимизации с полиморфизмом, поддерживаемый абстрактными типами. Поскольку это хорошая практика TDD, я пытаюсь написать все тесты оптимизации в абстрактном типе class(generic_optimizer), который затем должен запускаться каждым экземпляром класса, например, на type(newton_raphson).Ошибка сегментации с отложенными функциями и ключевое слово non_overridable

Все тесты оптимизации содержат вызов call my_problem%solve(...), который определяется как абстрактный тип и, конечно же, имеет различную реализацию в каждом производном типе.

Вопрос: если в каждом неабстрактном классе я определить функцию отложенной в 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 
+0

С быстрым взглядом нет очевидной причины, по которой атрибут 'non_overridable' запрещен (и более поздняя попытка переопределить). Однако у меня нет доступа к компилятору, чтобы воспроизвести проблему. – francescalus

+0

Похож на ошибку компилятора. – IanH

+0

Что это связано с Отсрочкой? –

ответ

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 

Я также попытался заменить функции с подпрограммами (для печати, которые подпрограммы на самом деле называются):

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), имеющий нулевой указатель, если это так, то история становится последовательным)


Редактировать

То же самое исключительное поведение gfortran происходит также, когда мы объявляем Type1, как abstract..В частности, если мы изменим определение 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 

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

Так в целом, кажется, что связывание имени square() в Type1 (который не имеет реализации) называется ошибочно по gfortran (возможно с нулем указатель). И что более важно, если мы опустим non_overridable из определения Type2, gfortran также дает 4.0 (без ошибки сегментации).

+0

очень интересно @roygvib, спасибо! Похоже, gfortran предполагает, что родительская процедура вызывается даже тогда, когда non_overridable указывается из расширенного типа. Чтение «Modern Fortran Explained» от Metcalf, Reid, Cohen, я обнаружил: - Если появляется атрибут 'non_overridable', эта процедура с привязкой к типу не может быть переопределена во время расширения типа; - 'non_overridable' несовместим с' отложенным', поскольку для этого требуется, чтобы процедура, связанная с типом, была переопределена; - Переопределение связанной с типом процедуры не допускается, если унаследованный имеет атрибут 'non_overridable'. –

+0

Привет, я снова рассмотрел ваш исходный код, но он по-прежнему смотрит на меня, что ваш код верен ... Я понимаю, что независимо от того, присоединяем ли мы 'non_overridable' к процедуре типа Type2, ее реализацию (т.е. Type2_square) следует вызывать из экземпляра Type2. (Я понимаю, что 'non_overridable' запрещает square() перезаписываться дочерним типом Type2 (скажем, Type3), поэтому он не влияет на поведение самого Type2.) – roygvib

+0

Что касается предложения« 'non_overridable', то несовместимо с «отложенным» в главе 14.6.1 «Modern Fortran Explained» (я купил версию Kindle :), я думаю, это означает, что мы не можем писать что-то вроде 'procedure (...), отложенного, non_overridable :: square ', т. е. мы не можем указывать два ключевых слова одновременно. С другой стороны, вполне вероятно, что использовать 'procedure (...), отложен :: square' для Type1 и' procedure, non_overridable :: square => Type2_square' для Type2 (возможно ...). – roygvib