C プログラムから呼び出される Fortran DLL があり、プロシージャの 1 つは、C プログラムによって提供されるコールバック関数を定期的に呼び出す必要があります。現在、「単純な」形式でうまく機能していますが、Fortran コード内でより簡単に受け渡しできるように、コールバック ポインターを派生型内に格納できるようにしたいと考えています。これまでのところ、私が試したことは何もないようです。
まず、これが私が現時点で持っているものであり、これはうまくいきます:
C (OK、実際には C++) プログラムで開始すると、コールバックのヘッダー プロトタイプは次のようになります。
typedef void (*fcb)(void *)
Fortran 呼び出しのプロトタイプは次のとおりです。
extern "C" __declspec(dllexport) int fortran_function(int n,
uchar *image_buffer,
fcb callback,
void *object);
実際のコールバック関数は次のとおりです。
void callback(void* pObject)
{
// Cast the void pointer back to the appropriate class type:
MyClass *pMyObject = static_cast<MyClass *>(pObject);
pMyObject -> updateImageInGUI();
}
C++ からの Fortran コードへの呼び出しは次のとおりです。
int error = fortran_function(m_image.size(), m_image.data, callback, this);
ここm_image
で、現在のオブジェクトのメンバー属性である画像データの配列です。C++ が生の画像データを Fortran DLL に渡し、Fortran にそれを処理するように要求します。これには長い時間がかかるため、Fortran は定期的に画像バッファーを更新し、コールバックを呼び出して GUI を更新します。とにかく、Fortran 側に移り、C コールバックのインターフェイスを定義します。
abstract interface
subroutine c_callback(c_object) bind(c)
use, intrinsic :: iso_c_binding
type(c_ptr), intent(in) :: c_object
end subroutine c_callback
end interface
メインの Fortran ルーチンを次のように定義します。
integer(c_int) fortran_function(n, image, callback, c_object) &
bind(c, name='fortran_function')
integer(c_int), value :: n
integer(4), intent(inout), dimension(n) :: image
procedure(c_callback) :: callback
type(c_ptr), intent(in) :: c_object
メインルーチンのどこかで、サブルーチンを呼び出しますfoo
:
call foo(data, callback, c_object)
...ここfoo
で、次のように定義されます。
subroutine foo(data, callback, c_object)
type(my_type), intent(inout) :: data
procedure(c_callback) :: callback
type(c_ptr), intent(in) :: c_object
...
call callback(c_object)
...
end function foo
私が言ったように、これはすべてうまく機能し、長い間そうしてきました.
今、私が試したがうまくいかないことについて:
引数を構造体のフィールドにコピーするだけの素朴なアプローチ
私がしているのは、元の要素を変更せずに構造にコピーすることだけなので、これが機能することを期待しています。C 側でも、主要な Fortran 関数の定義でも、 への抽象インターフェイスでも、何も変更されていませんc_callback
。新しい Fortran 派生型を作成するだけです。
type :: callback_data
procedure(c_callback), pointer, nopass :: callback => null()
type(c_ptr) :: c_object
end type callback_data
次に、メイン関数で、C アプリケーションから受け取った値を入力します。
data%callback_data%callback => callback
data%callback_data%c_object = c_object
call foo(data)
サブルーチン foo が少し変更され、構造内でコールバックと C オブジェクトを検索するようになりました。
subroutine foo(data)
type(my_augmented_type), intent(inout) :: data
...
call data%callback_data%callback(data%callback_data%c_object)
...
end function foo
これは、「アクセス違反読み取り場所 0xffffffffffffffff」の呼び出しで失敗します。
より多くの iso_c_binding 機能を使用した洗練されたアプローチ
C 側では何も変更しませんが、メイン関数の Fortran 側を次のようにコールバックを受け取るように変更しますc_funptr
。
integer(c_int) fortran_function(n, image, callback, c_object) &
bind(c, name='fortran_function')
integer(c_int), value :: n
integer(4), intent(inout), dimension(n) :: image
type(c_funptr), intent(in) :: callback
type(c_ptr), intent(in) :: c_object
抽象インターフェイスをsubroutine c_callback
前と同じように定義しますが、その部分を残したりbind(c)
、省略したりして実験しました。サブルーチンを呼び出すメイン関数内のコードは次のfoo
とおりです。
call c_f_procpointer(callback, data%callback_data%callback)
data%callback_data%c_object = c_object
call foo(data)
...サブルーチン foo 自体は、前の例のように定義されたままです。
残念ながら、これは前の例とまったく同じように失敗します。
ここで達成しようとしていることを達成するための正しい構文があると思います。アドバイスをいただければ幸いです。