Як працювати з функціональними покажчиками у Fortran у наукових програмах


11

Ось типове використання функціональних покажчиків у C. Я хотів би зробити щось подібне у Fortran. У мене є деякі ідеї, але я хотів би знати, чи є якийсь канонічний спосіб зробити це.

Показники функцій та контексти, передані користувачем, зберігаються, потім викликаються пізніше.

typedef PetscErrorCode (*TSIFunction)(TS,PetscReal,Vec,Vec,Vec,void*);
PetscErrorCode TSSetIFunction(TS ts,Vec res,TSIFunction f,void *ctx);

Функція користувача передзвонюється, використовуючи їх контекст у різні пізніші часи.

У PETSc вони також активно використовують таблиці вказівників string ->. Все є плагіном, тому користувач може зареєструвати власні реалізації та вони першокласні.

#define PCGAMG "gamg"
  PCRegisterDynamic(PCGAMG         ,path,"PCCreate_GAMG",PCCreate_GAMG);

Це реєструє процедуру створення у "FList", тоді PCSetFromOptions () пропонує можливість вибору цього методу порівняно з будь-яким з інших варіантів. Якщо система підтримує динамічне завантаження, ви можете пропустити залежність часу компіляції від символу PCCreate_GAMG і просто пропустити NULL, тоді символ буде шуканий у спільній бібліотеці під час виконання.

Зауважте, що цей крок за межі "фабрики" - це інверсія пристрою управління, аналогічна тому, що Мартін Фаулер називає "локатором обслуговування".

Зауважте: це з’явилось у моєму приватному листуванні з Джедом Брауном, де він задав мені це питання. Я вирішив передати це в аутсорсинг і подивитися, які відповіді люди можуть придумати.

Відповіді:


5

Немає необхідності використовувати передачу для емуляції void *в сучасному коді Fortran. Замість цього просто використовуйте внутрішній модуль ISO_C_BINDING , який підтримується всіма компіляторами Fortran Fortran. Цей модуль робить дуже простий інтерфейс між Fortran і C, з деякими дуже незначними застереженнями. Можна використовувати C_LOCі C_FUNLOCфункції, щоб отримати покажчики C на дані та процедури Fortran відповідно.

Що стосується вищевказаного прикладу PETSC, я припускаю, що контекст, як правило, вказує на визначену користувачем структуру, яка еквівалентна похідному типу даних у Fortran. Це не повинно бути проблемою для використання C_LOC. З непрозорою ручкою TSIFunction також дуже легко впоратися: просто використовуйте тип даних ISO_C_BINDING c_ptr, що еквівалентно знаку void *в C. Бібліотека, написана у Fortran, може використовувати, c_ptrякщо їй потрібно обробляти сувору перевірку сучасного Fortran.


Брайан, так, тим часом ми з Джедом розібралися у кількох рішеннях зворотного дзвінка, дивіться тут: fortran90.org/src/best-practices.html#type-casting-in-callbacks , тип (c_ptr) - номер розділу V.
Ondřej Čertík

9

Думаю, що у вашому питанні є багато, про що я здогадуюсь, певна мова PETSc (з якою я незнайома), тому тут може виникнути зморшка, яку я не дуже розумію, але, можливо, це все-таки стане корисним для вас розпочато.

В основному, ви повинні визначити інтерфейс для процедури, і тоді ви можете передати вказівник на функцію, яка слідує за цим інтерфейсом. У наведеному нижче коді показаний приклад. По-перше, існує модуль, який визначає інтерфейс і показує швидкий приклад фрагмента коду, який би виконав виконання процедури, наданої користувачем, що слідує за цим інтерфейсом. Далі йде програма, яка показує, як користувач використовував би цей модуль і визначає функцію, яку потрібно виконати.

MODULE xmod

  ABSTRACT INTERFACE
  FUNCTION function_template(n,x) RESULT(y)
      INTEGER, INTENT(in) :: n
      REAL, INTENT(in) :: x(n)
      REAL :: y
  END FUNCTION function_template
  END INTERFACE

CONTAINS

  SUBROUTINE execute_function(n,x,func,y)
    INTEGER, INTENT(in) :: n
    REAL, INTENT(in) :: x(n)
    PROCEDURE(function_template), POINTER :: func
    REAL, INTENT(out) :: y
    y = func(n,x)
  END SUBROUTINE execute_function

END MODULE xmod


PROGRAM xprog

  USE xmod

  REAL :: x(4), y
  PROCEDURE(function_template), POINTER :: func

  x = [1.0, 2.0, 3.0, 4.0]
  func => summation

  CALL execute_function(4,x,func,y)

  PRINT*, y  ! should give 10.0

CONTAINS

  FUNCTION summation(n,x) RESULT(y)
    INTEGER, INTENT(in) :: n
    REAL, INTENT(in) :: x(n)
    REAL :: y
    y = SUM(x)
  END FUNCTION summation

END PROGRAM xprog

Дякую за відповідь. Наведений вище приклад PETSc також зберігає вказівник функції в деякій внутрішній структурі даних, але я думаю, що зберігати PROCEDURE(function_template), POINTER :: funcвнутрішньо досить тривіально .
Ondřej Čertík

Зауважте, що вказівник є непрозорим об'єктом, а не адресою цього коду, тому AFAIK не може бути сумісним із C. У PETSc ми повинні підтримувати таблиці покажчиків функцій для обгортки C для цих речей.
Метт Кнеплі

Приклад PETSc зберігає як покажчик функції, так і контекст (приватні дані користувача, які передаються назад при виклику функції). Контекст дійсно важливий, інакше користувач закінчує робити жахливі речі, такі як посилання на глобальні. Оскільки немає еквівалента void*, користувачеві доводиться самостійно писати інтерфейси для функцій бібліотеки. Якщо ви реалізуєте бібліотеку на C, цього достатньо, але якщо ви реалізуєте у Fortran, вам слід забезпечити, щоб компілятор ніколи не бачив "фіктивну" INTERFACE бібліотеки одночасно з INTERFACE користувача.
Джед Браун

2
Еквівалент void*у Fortran - transferметод. Дивіться тут приклад використання. Інші 3 підходи, окрім transferметоду, це "робочі масиви", "конкретний похідний тип замість void *" та використання змінних модулів, локальних для модуля.
Ondřej Čertík

Прикро користувачеві доводиться гнатися з transferдурницею типу ( character (len=1), allocatable) просто для виклику функції.
Джед Браун
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.