Bilimsel programlarda Fortran'daki işlev işaretçileriyle nasıl çalışılır


11

Burada C'deki işlev işaretlerinin tipik bir kullanımı. Fortran'da benzer bir şey yapmak istiyorum. Bazı fikirlerim var, ama bunu yapmanın bazı kanonik yolları olup olmadığını bilmek istiyorum.

Kullanıcı tarafından iletilen işlev işaretçileri ve bağlamları saklanır ve daha sonra çağrılır.

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

Kullanıcının işlevi, daha sonraki çeşitli zamanlarda bağlamları kullanılarak geri çağrılır.

PETSc'de, string -> fonksiyon işaretçi tablolarını da yoğun bir şekilde kullanırlar. Her şey bir eklentidir, böylece kullanıcı kendi uygulamalarını kaydedebilir ve birinci sınıftır.

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

Bu, oluşturma rutinini bir "FList" e kaydeder, ardından PCSetFromOptions () bu yöntemi diğer seçeneklerden herhangi birine göre seçme olanağı sunar. Sistem dinamik yüklemeyi destekliyorsa, PCCreate_GAMG sembolüne derleme zamanı bağımlılığını atlayabilir ve sadece NULL iletebilirsiniz, ardından sembol çalışma zamanında paylaşılan kitaplıkta aranacaktır.

Bu bir "fabrika" ötesinde bir adım ötesinde, Martin Fowler "servis bulucu" olarak adlandırılan benzer bir kontrol cihazının ters olduğunu unutmayın.

Not: Bu, Jed Brown ile özel yazışmamda ortaya çıktı, burada bu soruyu bana sordu. Dış kaynak kullanmaya ve insanların hangi cevapları bulabileceğini görmeye karar verdim.

Yanıtlar:


5

void *Modern bir Fortran kodunda taklit etmek için transfer kullanmaya gerek yoktur . Bunun yerine, tüm ana Fortran derleyicileri tarafından desteklenen ISO_C_BINDING iç modülünü kullanın . Bu modül, Fortran ve C arasında arayüz oluşturmayı çok küçük uyarılarla kolaylaştırır. Fortran veri ve prosedürlerine sırasıyla C işaretçileri almak için C_LOCve C_FUNLOCfonksiyonlarını kullanabilirsiniz .

Yukarıdaki PETSC örneğiyle ilgili olarak, bağlamın tipik olarak Fortran'daki türetilmiş bir veri türüne eşdeğer olan kullanıcı tanımlı bir yapıya bir işaretçi olduğunu varsayıyorum. Bu kullanımı ile ilgili bir sorun olmamalı C_LOC. Opak TSIFunction tanıtıcısı da başa çıkmak için çok basittir: sadece C'de c_ptreşdeğer olan ISO_C_BINDING veri türünü void *kullanın. Fortran'da yazılmış bir kütüphane, c_ptrmodern Fortran'ın katı tip denetimi etrafında çalışması gerekiyorsa kullanabilir .


Brian, evet, bu arada, Jed ve ben geri aramaya birkaç çözüm bulduk, buraya bakın: fortran90.org/src/best-practices.html#type-casting-in-callbacks , tip (c_ptr) V numaralı bölümdür.
Ondřej Čertík

9

Sorunuzda PETSc'ye özgü bir dil olduğunu tahmin ettiğim çok şey var (bilmediğim), bu yüzden burada tam olarak anlamadığım bir kırışıklık olabilir, ancak belki de bu sizi elde etmek için yararlı olacaktır başladı.

Temel olarak, yordam için arabirimi tanımlamanız gerekir ve sonra bu arabirimi izleyen bir işleve bir işaretçi iletebilirsiniz. Aşağıdaki kod bir örneği göstermektedir. İlk olarak, arabirimi tanımlayan ve bu arabirimi izleyen kullanıcı tarafından sağlanan rutini yürütecek bir kod yığınına hızlı bir örnek gösteren bir modül vardır. Sonraki, kullanıcının bu modülü nasıl kullanacağını ve yürütülecek işlevi nasıl tanımlayacağını gösteren bir programdır.

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

Cevap için teşekkürler. Yukarıdaki PETSc örneği de işlev işaretçisini bazı dahili veri yapısında saklar, ancak PROCEDURE(function_template), POINTER :: funcdahili olarak kaydetmenin oldukça önemsiz olduğunu düşünüyorum .
Ondřej Čertík

İşaretçinin bu kodun adresinden ziyade opak bir nesne olduğuna dikkat edin, bu yüzden AFAIK, C ile birlikte çalışamaz olamaz. PETSc'de, bu şeyler için C sarmalayıcıları için işlev işaretçileri tablolarını tutmamız gerekir.
Matt Knepley

PETSc örneği hem işlev işaretçisini hem de içeriği (işlev çağrıldığında geri iletilen özel kullanıcı verileri) depolar. Bağlam gerçekten çok önemlidir, aksi takdirde kullanıcı küresellere referans vermek gibi korkunç şeyler yapar. Buna eşdeğer olmadığından void*, kullanıcı kütüphane fonksiyonları için arayüzler yazmak zorunda kalır. Kütüphaneyi C'de uygularsanız bu yeterlidir, ancak Fortran'da uygularsanız, derleyicinin hiçbir zaman kütüphanenin "kukla" ARAYÜZÜNÜ kullanıcının ARAYÜZÜ ile aynı anda görmemesini sağlamanız gerekir.
Jed Brown

2
void*Fortran'da eşdeğer bir transferyöntemdir. Kullanım örneği için buraya bakın . transferYöntemin yanı sıra diğer 3 yaklaşım "çalışma dizileri", "özel tür yerine void *" yerine ve modüle yerel modül değişkenlerini kullanmaktır.
Ondřej Čertík

Kullanıcının sadece fonksiyonu çağırmak için transfersaçma bir tip ( character (len=1), allocatable) ile uğraşması bir utanç .
Jed Brown
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.