วิธีทำงานกับพอยน์เตอร์ฟังก์ชั่นใน Fortran ในโปรแกรมวิทยาศาสตร์


11

นี่คือการใช้งานทั่วไปของตัวชี้ฟังก์ชันใน C. ฉันต้องการทำสิ่งที่คล้ายกันใน Fortran ฉันมีความคิดบางอย่าง แต่ฉันต้องการที่จะรู้ว่ามีวิธีที่ยอมรับได้หรือไม่

พอยน์เตอร์ของฟังก์ชั่นและบริบทที่ผ่านเข้ามาโดยผู้ใช้จะถูกเก็บไว้จากนั้นเรียกในภายหลัง

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

ฟังก์ชั่นของผู้ใช้จะถูกเรียกคืนโดยใช้บริบทของพวกเขาในเวลาต่อมา

ใน PETSc พวกเขายังใช้ประโยชน์จากตารางตัวชี้ฟังก์ชัน -> ทุกอย่างเป็นปลั๊กอินดังนั้นผู้ใช้สามารถลงทะเบียนการใช้งานของตัวเองและพวกเขาเป็นชั้นหนึ่ง

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

สิ่งนี้ลงทะเบียนรูทีนการสร้างใน "FList" จากนั้น PCSetFromOptions () จะเสนอความสามารถในการเลือกวิธีนี้เมื่อเทียบกับตัวเลือกอื่น ๆ หากระบบรองรับการโหลดแบบไดนามิกคุณสามารถข้ามการพึ่งพาเวลาคอมไพล์ในสัญลักษณ์ PCCreate_GAMG และเพิ่งผ่าน NULL จากนั้นสัญลักษณ์จะถูกค้นหาในไลบรารีที่แบ่งใช้ในขณะใช้งาน

โปรดทราบว่าหนึ่งขั้นตอนนี้เกินกว่า "โรงงาน" มันเป็นสิ่งที่ตรงกันข้ามกับอุปกรณ์ควบคุมที่คล้ายกับสิ่งที่ Martin Fowler เรียกว่า "service locator"

หมายเหตุ: สิ่งนี้เกิดขึ้นในการติดต่อส่วนตัวของฉันกับเจดบราวน์ที่ซึ่งเขาถามคำถามนี้ให้ฉัน ฉันตัดสินใจที่จะ outsource มันและดูว่าผู้คนสามารถหาคำตอบได้อย่างไร

คำตอบ:


5

ไม่จำเป็นต้องใช้การโอนเพื่อเลียนแบบvoid *ในรหัส Fortran ที่ทันสมัย ให้ใช้โมดูล Intrinsic ISO_C_BINDINGซึ่งได้รับการสนับสนุนจากคอมไพเลอร์ 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 wrappers สำหรับสิ่งเหล่านี้
Matt Knepley

ตัวอย่าง PETSc จะเก็บทั้งตัวชี้ฟังก์ชันและบริบท (ข้อมูลผู้ใช้ส่วนตัวที่ถูกส่งกลับเมื่อเรียกใช้ฟังก์ชัน) บริบทเป็นสิ่งสำคัญมากมิฉะนั้นผู้ใช้จะทำสิ่งที่น่ากลัวเช่นการอ้างอิงถึงกลุ่มคน เนื่องจากไม่มีสิ่งใดเทียบเท่าvoid*ผู้ใช้จึงต้องเขียนส่วนต่อประสานสำหรับฟังก์ชั่นห้องสมุดด้วยตนเอง หากคุณใช้งานไลบรารีใน C สิ่งนี้ก็เพียงพอแล้ว แต่ถ้าคุณใช้งานใน Fortran คุณต้องตรวจสอบให้แน่ใจว่าคอมไพเลอร์ไม่เคยเห็น INTERFACE "จำลอง" ของไลบรารีในเวลาเดียวกันกับ INTERFACE ของผู้ใช้
Jed Brown

2
เทียบเท่ากับvoid*ใน Fortran เป็นtransferวิธีการ ดูที่นี่สำหรับตัวอย่างของการใช้ อีก 3 วิธีที่นอกเหนือจากtransferวิธีนี้คือ "work arrays", "ชนิดที่ได้รับเฉพาะแทนvoid *" และใช้ตัวแปรโมดูลเฉพาะที่ในโมดูล
OndřejČertík

มันเป็นเรื่องน่าละอายที่ผู้ใช้จะต้องเข้าไปยุ่งเกี่ยวกับtransferเรื่องไร้สาระ ( character (len=1), allocatable) เพียงเพื่อเรียกใช้ฟังก์ชัน
Jed Brown
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.