Как я могу иметь указатель процедуры Fortran в качестве компонента производного типа, который указывает на процедуру, связанную с типом, в расширении этого типа?

Я проверил подобные вопросы и соответствующие учебники, но не могу найти решение этой проблемы.

NB: здесь используется современный Fortran.

Итак, я хочу иметь базовый тип, например. base, который содержит процедуру с привязкой к типу, например. run, который делегирует подпрограмме типы, расширяющие базу, например. type, extends(base) :: my_extension. Давайте вызовем подпрограмму, которой мы хотим делегировать my_procedure.

Я могу добиться этого довольно запутанным способом, сделав base абстрактным, заставив base содержать отложенную процедуру, например. delegate, получение run для делегирования delegate, затем реализация delegate в my_extension и его делегирование my_procedure.

Однако это беспорядочно, так как для реализации delegate требуются типы, расширяющие base, но все реализации просто делегируют какую-то другую процедуру. Это еще более усложняется тем фактом, что я обычно хочу несколько экземпляров my_extension, где каждый экземпляр делегирует другую процедуру в my_extension, и в этом случае мне нужно сохранить my_extension абстрактным, расширить my_extension и реализовать delegate там, чтобы я получил столько расширений. как у меня процедуры!

Итак, мне интересно, есть ли более чистый способ использования указателей процедур или подобных. Я хочу что-то вроде этого...

база.f90:

module base_mod

    implicit none

    type base
        procedure(my_interface), pointer :: ptr  ! Pointer component
    contains
        procedure :: run  ! Type-bound procedure
    end type base

    abstract interface
        subroutine my_interface(self)
            import :: base
            class(base) :: self  ! This is a problem...
        end subroutine my_interface
    end interface


contains

    subroutine run(self)
        class(base) :: self
        call self%ptr()
    end subroutine run


end module base_mod

мое_расширение.f90:

module my_extension_mod

    use base_mod

    implicit none

    type, extends(base) :: my_extension
    contains
        procedure :: my_procedure
    end type my_extension


contains

    subroutine my_procedure(self)
        class(my_extension) :: self  ! ...because this is different.
        ! Do useful stuff, e.g.
        print *, "my_procedure was run"
    end subroutine my_procedure

end module my_extension_mod

основной.f90:

program main

    use my_extension_mod

    implicit none

    type(my_extension) :: my_instance
    procedure(my_interface), pointer :: my_ptr
    my_ptr => my_procedure
    my_instance = my_extension(my_ptr)  ! Use implicit constructor
    call my_instance%run()  ! Expect to see "my_procedure was run" printed

end program main

сделать.ш:

#! /bin/bash
gfortran -c base.f90
gfortran -c my_extension.f90
gfortran base.o my_extension.o main.f90

Однако компилятор жалуется на следующее:

main.f90:9.14:

    my_ptr => my_procedure
          1
Error: Interface mismatch in procedure pointer assignment at (1): Type/rank mismatch in argument 'self'

Это связано с тем, что my_interface ожидает объект класса base в качестве входных данных, тогда как my_procedure имеет в качестве входных данных объект класса my_extension, поэтому технически интерфейсы не совпадают. Тем не менее, my_extension расширяет base, так что в некотором смысле это должно быть нормально (но очевидно, что это не так с точки зрения компилятора).

Итак, мой вопрос: как я могу решить эту проблему? Как я могу иметь интерфейс, который работает для my_procedure, но по-прежнему работает для других процедур в других типах, которые расширяют base (например, в my_other_extension)? Или как я могу иметь указатели на процедуры в расширениях base без использования интерфейса? Или как я могу достичь того, что я пытаюсь сделать другим способом (например, без использования указателей), но при этом избегая запутанного метода абстрактного типа, описанного выше?


person Biggsy    schedule 16.05.2018    source источник
comment
У меня есть некоторое сочувствие к вопросу, но я не совсем уверен в вашей цели. Почему вам особенно необходимо иметь компонент указателя, указывающий на то же самое, что и связанная процедура? Бывают случаи, когда это полезно/необходимо, но почему бы не делать это в исключительных случаях, когда это необходимо.   -  person francescalus    schedule 16.05.2018
comment
Скажем, у меня есть 2 типа, которые расширяют base, называемые my_extension и my_other_extension. У каждого есть 2 процедуры: my_extension_procedure_one, my_extension_procedure_two, my_other_extension_procedure_one и my_other_extension_procedure_two. Я хочу создать 4 объекта класса base, где указатель в 1-м объекте указывает на my_extension_procedure_one, указатель во 2-м объекте указывает на my_extension_procedure_two и т. д. Затем я могу сохранить их в массиве объектов base и вызвать run для каждого, который будет по сути, вызовите каждую из 4 процедур один раз. Это имеет больше смысла?   -  person Biggsy    schedule 16.05.2018
comment
Думаю, я понимаю это. Я не могу предложить разумный подход (отметив, что у вас действительно будут проблемы с вашим вопросом), но прежде чем я усердно работал над этим, я бы беспокоился о том, что мне все еще приходится настраивать эти указатели вручную ( ptr(1)=>type1%proc1; ptr(2)=>type1%proc2; ptr(3)=>type2%proc1; ptr(4)=>type2%proc2) так что, возможно, этот массив меня мало спасет.   -  person francescalus    schedule 16.05.2018
comment
Это правда. Но... а) все это в одном месте, вместо расширения типа для каждой процедуры; б) В случае расширения типа для каждой процедуры все равно необходимо построить их массив; и (самое главное) c) я намереваюсь написать препроцессор исходного кода fortran на каком-то другом языке, который может автоматически генерировать список указателей и записывать их в исходный файл fortran (так что чистое решение упростит реализацию препроцессора).   -  person Biggsy    schedule 16.05.2018
comment
В чем смысл привязки my_procedure в расширении? Нужно ли привязывать процедуру my_procedure к этому типу?   -  person IanH    schedule 16.05.2018
comment
Хорошая точка зрения. Я подумал об этом, но, к сожалению, да, процедуры в my_extension должны быть привязаны к типу. Это связано с тем, что они будут вызывать другие процедуры с привязкой к типу в base, которые изменяют состояние компонентов base. Если my_extension_procedure_one и т. д. не привязаны к типу, то объекты base должны быть переданы, что негативно влияет на API my_extension_procedure_one и т. д. хуже, чем исходная проблема my_extension и т. д. должна быть абстрактной.   -  person Biggsy    schedule 17.05.2018
comment
Обратите внимание, что использование расширения файла .f08 не рекомендуется, и некоторые компиляторы отклонят его без специальных параметров intel-fortran-compiler" title="правильный суффикс для исходного файла fortran 2003 компилятор intel fortran">stackoverflow.com/questions/20269076/ Будете ли вы переименовывать все свои файлы, как только добавите какую-либо функцию Fortran 2015?   -  person Vladimir F    schedule 17.05.2018
comment
Хорошо, спасибо, хороший совет. Я обновил вопрос и обновлю свои файлы.   -  person Biggsy    schedule 17.05.2018
comment
Ваше объяснение, почему процедура должна быть привязана к типу, для меня не имеет смысла. Привязка к типу не делает процедуру особенной с точки зрения того, что она может или не может делать (потому что они будут вызывать...). Привязки связаны с динамической отправкой (какая процедура вызывается во время выполнения при ссылке на привязку) и управлением пространством имен.   -  person IanH    schedule 17.05.2018
comment
Это правда, что с точки зрения динамической диспетчеризации им не обязательно быть процедурами с привязкой к типу. Вот почему я подумал о том, чтобы сделать их не привязанными к типу, как это было предложено. Однако в этом случае необходимо передать объекты типа base в my_extension_procedure_one и т. д., чтобы можно было изменить их состояние. Это создает беспорядок в API, поскольку пользователи не хотят писать процедуры, которые получают объект base. Я бы сказал, что в объектно-ориентированном программировании есть нечто большее, чем просто динамическая диспетчеризация — это также инкапсуляция, которую я пытаюсь здесь сделать.   -  person Biggsy    schedule 17.05.2018
comment
Нет, для динамической отправки она ДОЛЖНА БЫТЬ вызвана как процедура с привязкой к типу! В противном случае вы вызываете подпрограмму, которую вызываете, и ничего динамического не видно.   -  person Vladimir F    schedule 23.05.2018