2016-04-12 50 views
5

Potrzebuję w podręczniku w języku Fortran wskaźnik proceduralny, który może wskazywać na jeden z wielu podprogramów. Problem ten wydaje się być powszechne na SO:Wskaźnik procedury Fortran do podprogramów w typach pochodnych

Fortran save procedure as property in derived type

Type bound procedure overloading in Fortran 2003

There is no matching specific subroutine for this type bound generic subroutine call

Generic type-bound procedures with procedure arguments

Type bound procedure as arguments

aby wymienić tylko kilka. Odpowiedź na to pytanie dla funkcji jest bardzo ładnie przedstawiona w pierwszym odnośniku.

Jednak nadal nie jestem pewien co do metodologii opracowania takiego kodu również w przypadku, gdy wskaźnik procedury związany z typem wskazuje na podprogram. Trudność wydaje się, że nie ma typu związanego z tym, co jest zwracane (ponieważ nic tak naprawdę nie jest "zwracane").

Chciałbym również podkreślić, że choć proste rozwiązanie może istnieć w nowszym standardzie fortuna (2003,2008), to rozwiązanie może nie działać na wszystkich kompilatorach, co może być problematyczne w przyszłości . Więc interesują mnie rozwiązania przyjazne dla kompilatorów.

Mam mały kod (pokazany poniżej), który obecnie działa, ale w moim dużym kodzie dostaję wewnętrzny błąd kompilatora (pokazany również poniżej) w pliku, w którym używam wskaźników procedur w typach pochodnych. Moje pytanie brzmi: co mogę zrobić, aby poniżej kod do

1) Ściśle używać jawnych interfejsy

2) Maksymalizacja informacje przekazywane do kompilatora

3) zapewnienia kod jest przenośny między tak wielu kompilatorów w miarę możliwości (tj. użyj fortranowych standardów 90/95).

W jakim stopniu powyższe może być spełnione (1 jest najważniejszy)? Czy powyższe kryteria są spełnione? Wiem, że to "spełnia wszystkie te kryteria" jest subiektywne, ale twierdzę, że odpowiedź brzmi "tak" dla tego samego pytania dotyczącego funkcji zamiast podprogramów.

gcc version 5.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 

Mała Kod:

module subs_mod 
    implicit none 
    public :: add,mult 
    contains 
    subroutine add(x,y,z) 
    implicit none 
    integer,intent(inout) :: x 
    integer,intent(in) :: y,z 
    x = y+z 
    end subroutine 
    subroutine mult(x,y,z) 
    implicit none 
    integer,intent(inout) :: x 
    integer,intent(in) :: y,z 
    x = y*z 
    end subroutine 
    end module 

    module type_A_mod 
    use subs_mod 
    implicit none 
    public :: type_A,init,operate 
    type type_A 
    procedure(),pointer,nopass :: op 
    end type 
    contains 
    subroutine init(A,op) 
    implicit none 
    external :: op 
    type(type_A),intent(inout) :: A 
    A%op => op 
    end subroutine 
    subroutine operate(A,x,y,z) 
    implicit none 
    type(type_A),intent(in) :: A 
    integer,intent(inout) :: x 
    integer,intent(in) :: y,z 
    call A%op(x,y,z) 
    end subroutine 
    end module 

    program test 
    use type_A_mod 
    use subs_mod 
    implicit none 
    type(type_A) :: A 
    integer :: x 
    call init(A,mult) 
    call operate(A,x,3,5) 
    write(*,*) 'x = ',x 
    end program 

błąd kompilatora w dużym kodu:

f951.exe: internal compiler error: Segmentation fault 
    libbacktrace could not find executable to open 
    Please submit a full bug report, 
    with preprocessed source if appropriate. 
    See <http://sourceforge.net/projects/mingw-w64> for instructions. 

UPDATE

Oto mała modyfikacja, która daje kompilator więcej informacji, ale Nie próbowałem tego na wielkim kodzie. Wydaje się jednak arbitralne i nie mam pojęcia, czy to pomoże, czy nie.

... 
    function add(x,y,z) result(TF) 
    ... 
    logical :: TF 
    x = y+z 
    TF = .true. 
    end function 
    function mult(x,y,z) result(TF) 
    ... 
    logical :: TF 
    x = y*z 
    TF = .true. 
    end function 
    end module 

    module type_A_mod 
    ... 
    type type_A 
    procedure(logical),pointer,nopass :: op 
    end type 
    ... 
    subroutine init(A,op) 
    implicit none 
    logical,external :: op 
    ... 
    end subroutine 
    subroutine operate(A,x,y,z) 
    ... 
    logical :: TF 
    TF = A%op(x,y,z) 
    end subroutine 
    end module 

    program test 
    ... 
    end program 

ROZWIĄZANIE UWAGI tylko skomentować roztworu (dostarczonych przez @IanH) był jeden dodatkowy zmarszczek, a było to, że niektóre rodzaje otrzymane wprowadzania streszczenie interfejs, który według The New Features of Fortran 2003, należy dołączyć instrukcję Import, aby abstrakcyjny interfejs był świadomy wszelkich wprowadzanych typów pochodnych. Oto mały przykład roboczych, które stosuje się do wielkiego kodu łagodzi wewnętrzny błąd kompilatora byłem mający :)

module DT_mod 
    implicit none 
    private 
    public :: DT 
    type DT 
    integer :: i 
    end type 
    contains 
    end module 

    module subs_mod 
    use DT_mod 
    implicit none 
    private 
    public :: add,mult,op_int 

    abstract interface 
    subroutine op_int(d,x,y,z) 
    import :: DT 
    implicit none 
    type(DT),intent(inout) :: d 
    integer,intent(inout) :: x 
    integer,intent(in) :: y,z 
    end subroutine 
    end interface 

    contains 
    subroutine add(d,x,y,z) 
    implicit none 
    type(DT),intent(inout) :: d 
    integer,intent(inout) :: x 
    integer,intent(in) :: y,z 
    x = y+z 
    d%i = 1 
    end subroutine 
    subroutine mult(d,x,y,z) 
    implicit none 
    type(DT),intent(inout) :: d 
    integer,intent(inout) :: x 
    integer,intent(in) :: y,z 
    x = y*z 
    d%i = 2 
    end subroutine 
    end module 

    module type_A_mod 
    use DT_mod 
    use subs_mod 
    implicit none 
    private 
    public :: type_A,init,operate 
    type type_A 
    procedure(op_int),pointer,nopass :: op 
    end type 
    contains 
    subroutine init(A,op) 
    implicit none 
    procedure(op_int) :: op 
    type(type_A),intent(inout) :: A 
    A%op => op 
    end subroutine 
    subroutine operate(A,d,x,y,z) 
    implicit none 
    type(DT),intent(inout) :: d 
    type(type_A),intent(in) :: A 
    integer,intent(inout) :: x 
    integer,intent(in) :: y,z 
    call A%op(d,x,y,z) 
    end subroutine 
    end module 

    program test 
    use type_A_mod 
    use subs_mod 
    use DT_mod 
    implicit none 
    type(type_A) :: A 
    type(DT) :: d 
    integer :: x,y,z 
    y = 3; z = 5 
    call init(A,mult) 
    call operate(A,d,x,y,z) 
    write(*,*) 'x,y,x = ',y,z,x 
    write(*,*) 'd%i = ',d%i 
    end program 

Każda pomoc jest mile widziana.

+0

Zastanów się, pokazując różnicę, trudno jest zobaczyć, co zmieniłeś w aktualizacji 2, szczególnie biorąc pod uwagę, że nie używasz żadnych pustych linii w kodzie (co utrudnia odczyt). –

+0

BTW, spróbuj '-std = f95' w gfortran, a zobaczysz, ile rzeczy faktycznie jest Fortran 2003 i później. –

+0

Dzięki @VladimirF, niedawno wypróbowałem to, to było pomocne. – Charlie

Odpowiedz

5

Wskaźniki do procedur nie były częścią standardowego języka do wersji Fortran 2003, więc jeśli chcesz z nich korzystać w ogóle, zgodność z Fortran 95 jest nieistotna.

Wewnętrzny błąd kompilatora jest błędem kompilatora, niezależnie od źródła podanego kompilatorowi.

Nie istnieje coś takiego jak wskaźnik procedury związany z typem. Masz albo procedurę związaną z typem - która jest zadeklarowana po CONTAINS w konstrukcie typu pochodnego, albo masz wskaźnik procedury - który może być komponentem typu lub obiektu autonomicznego. Wskaźnik procedury, który jest składnikiem, jest częścią wartości obiektu typu pochodnego - może być powiązany z różnymi procedurami w środowisku wykonawczym. Procedura związana z typem jest stałą właściwością deklaracji typu.

Jeśli chcesz, aby wskaźnik procedury (lub sztuczna procedura) miał jawny interfejs, musisz podać nazwę interfejsu w nawiasie instrukcji deklaracji procedury.

procedure(interface_name_goes_here) [, pointer, ...] :: thing_being_declared 

Nazwa interfejsu pod warunkiem może być nazwa dostępnej procedury określonej (w tym jeden uprzednio zadeklarowanej przez procedury innego oświadczenia deklaracji), albo imię abstrakcyjnego interfejsu.

(Jeśli nazwa interfejsu w deklaracji deklaracji procedury jest typem, podobnie jak w przypadku komponentu w kodzie przykładowym, zadeklarowana procedura jest funkcją z wynikiem danego typu, z niejawnym interfejsem.

Jeśli brakuje nazwy interfejsu w deklaracji procedury, procedura, która jest zadeklarowana, może być funkcją lub podprocedłem (którego późniejszy użytek musi być zgodny z jednym lub drugim) z niejawnym interfejsem.)

Zakładając, że chcesz zadeklarować element wskaźnika procedury z jawnym interfejsem do funkcji (w przeciwieństwie do tytułu pytania) o tej samej charakterystyce s jak add lub mult w drugim odcinku kodu:

TYPE type_A 
    PROCEDURE(the_interface), POINTER, NOPASS :: op 
END TYPE type_A 

ABSTRACT INTERFACE 
    FUNCTION the_interface(x, y, z) RESULT(tf) 
    IMPLICIT NONE 
    ! function modifying arguments - poor style!!! 
    INTEGER, INTENT(INOUT) :: x 
    INTEGER, INTENT(IN) :: y, z 
    LOGICAL :: tf 
    END FUNCTION the_interface 
END INTERFACE 

Jeśli chcesz wskaźnik procedura będzie podprogram z wyraźnym interfejs (co jest korzystne dla funkcji, która modyfikuje jego argumenty) - zmiana abstrakcyjny interfejs odpowiednio.

Procedura manekin w init podprogramu nie musi być wskaźnikiem - wewnątrz init nie zmieniają co odniesienia rzeczą op - jesteś po prostu wskazując inny wskaźnik na niego:

PROCEDURE(the_interface) :: op 

Kiedy swój manekina procedury i wskaźniki procedur są deklarowane z jawnym interfejsem, oczekiwałbym, że rozsądny kompilator zdiagnozuje jakiekolwiek niedopasowania w charakterystyce.

+0

Wygląda to bardzo obiecująco. Mam jeszcze jeden problem z tą implementacją. Kiedy próbuję zainicjować procedurę w moim dużym kodzie (wywołanie init (A, mult) w małym kodzie), pojawia się błąd z informacją "Niezgodność interfejsu w procedurze fikcyjnej procedury" w punkcie (1): BRAK INTENTYFIKACJI argument "x" ". Powiedziałem, że udało mi się teraz skompilować plik, który wcześniej spowodował wewnętrzny błąd kompilatora. – Charlie

+0

To działało idealnie. Dziękuję Ci. – Charlie