Cách làm việc với các con trỏ hàm trong Fortran trong các chương trình khoa học


11

Dưới đây là cách sử dụng điển hình của các con trỏ hàm trong C. Tôi muốn làm một cái gì đó tương tự trong Fortran. Tôi có một số ý tưởng, nhưng tôi muốn biết nếu có một số cách kinh điển để làm như vậy.

Các con trỏ hàm và bối cảnh được người dùng truyền vào được lưu trữ, sau đó được gọi sau.

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

Chức năng của người dùng được gọi lại bằng cách sử dụng bối cảnh của họ tại các thời điểm khác nhau.

Trong PETSc, họ cũng sử dụng rất nhiều chuỗi -> bảng con trỏ hàm. Tất cả mọi thứ là một plugin, vì vậy người dùng có thể đăng ký triển khai của riêng họ và họ là hạng nhất.

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

Điều này đăng ký thói quen tạo trong một "FList", sau đó PCSetFromOptions () cung cấp khả năng chọn phương thức này so với bất kỳ lựa chọn nào khác. Nếu hệ thống hỗ trợ tải động, bạn có thể bỏ qua sự phụ thuộc thời gian biên dịch vào biểu tượng PCCreate_GAMG và chỉ cần vượt qua NULL, sau đó biểu tượng sẽ được tra cứu trong thư viện dùng chung trong thời gian chạy.

Lưu ý rằng bước này vượt ra khỏi một "nhà máy", đó là một thiết bị điều khiển đảo ngược tương tự như cái mà Martin Fowler gọi là "công cụ định vị dịch vụ".

Lưu ý: điều này xuất hiện trong thư từ riêng tư của tôi với Jed Brown, nơi anh ấy hỏi tôi câu hỏi này. Tôi quyết định thuê ngoài nó và xem những câu trả lời mà mọi người có thể đưa ra.

Câu trả lời:


5

Không cần sử dụng chuyển để mô phỏng void *theo mã Fortran hiện đại. Thay vào đó, chỉ cần sử dụng mô-đun nội tại ISO_C_BINDING , được hỗ trợ bởi tất cả các trình biên dịch Fortran chính thống. Mô-đun này làm cho giao diện giữa Fortran và C rất đơn giản, với một số cảnh báo rất nhỏ. Người ta có thể sử dụng các hàm C_LOCC_FUNLOChàm để đưa các con trỏ C vào dữ liệu và thủ tục của Fortran, tương ứng.

Đối với ví dụ về PETSC ở trên, tôi giả sử bối cảnh thường là một con trỏ tới cấu trúc do người dùng xác định, tương đương với kiểu dữ liệu dẫn xuất trong Fortran. Đó không phải là một vấn đề để đối phó với việc sử dụng C_LOC. Tay cầm TSIFeft mờ cũng rất đơn giản để xử lý: chỉ cần sử dụng kiểu dữ liệu ISO_C_BINDING c_ptr, tương đương với void *C. Một thư viện được viết bằng Fortran có thể sử dụng c_ptrnếu cần làm việc xung quanh việc kiểm tra loại nghiêm ngặt của Fortran hiện đại.


Brian, vâng, trong khi đó, Jed và tôi đã tìm ra khá nhiều giải pháp cho cuộc gọi lại, xem tại đây: fortran90.org/src/best-practices.html#type-casting-in-callbacks , loại (c_ptr) là phần số V.
Ondřej ertík

9

Có rất nhiều điều tôi đoán là ngôn ngữ dành riêng cho PETSc trong câu hỏi của bạn (mà tôi không quen), vì vậy có thể có một nếp nhăn ở đây tôi không hiểu lắm, nhưng có lẽ điều này vẫn hữu ích để giúp bạn đã bắt đầu.

Về cơ bản, bạn phải xác định giao diện cho thủ tục và sau đó bạn có thể chuyển một con trỏ tới một hàm theo giao diện này. Các mã sau đây cho thấy một ví dụ. Đầu tiên, có một mô-đun xác định giao diện và hiển thị một ví dụ nhanh về một đoạn mã sẽ thực thi thói quen được cung cấp bởi người dùng theo giao diện đó. Tiếp theo là một chương trình cho thấy người dùng sẽ sử dụng mô-đun này như thế nào và xác định chức năng sẽ được thực thi.

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

Cảm ơn câu trả lời. Ví dụ PETSc ở trên cũng lưu trữ con trỏ hàm trong một số cấu trúc dữ liệu nội bộ, nhưng tôi nghĩ rằng việc lưu PROCEDURE(function_template), POINTER :: funcnội bộ là khá nhỏ .
Ondřej Čertík

Lưu ý rằng con trỏ là một đối tượng mờ chứ không phải là địa chỉ của mã đó, vì vậy AFAIK không thể có khả năng tương tác với C. Trong PETSc, chúng ta phải duy trì các bảng con trỏ hàm cho trình bao C cho những điều này.
Matt Knepley

Ví dụ PETSc lưu trữ cả con trỏ hàm và ngữ cảnh (dữ liệu người dùng riêng được truyền lại khi hàm được gọi). Bối cảnh là rất quan trọng, nếu không, người dùng cuối cùng sẽ làm những điều khủng khiếp như tham chiếu toàn cầu. Vì không có tương đương void*, người dùng cuối cùng phải tự viết giao diện cho các chức năng của thư viện. Nếu bạn triển khai thư viện trong C, điều này là đủ, nhưng nếu bạn triển khai trong Fortran, bạn phải đảm bảo rằng trình biên dịch không bao giờ thấy INTERFACE "giả" của thư viện cùng lúc với INTERFACE của người dùng.
Jed Brown

2
Tương đương void*trong Fortran là một transferphương pháp. Xem ở đây để biết ví dụ về việc sử dụng. Ba cách tiếp cận khác bên cạnh transferphương thức là "mảng công việc", "loại dẫn xuất cụ thể thay vì void *" và sử dụng các biến mô-đun cục bộ cho mô-đun.
Ondřej Čertík

Thật xấu hổ khi người dùng phải làm quen với transfermột loại vô nghĩa ( character (len=1), allocatable) chỉ để gọi hàm.
Jed Brown
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.