1

requireLuaは、またはを使用して.dllを動的にロードできますpackage.loadlib。ただし、モジュールをアンロードする機能はありません。

実行中のプログラムでモジュールを更新できるようにしたい。プログラム自体は、.dllの新しいバージョンについて通知されます。次に、新しいテーブルをロードし、モジュールテーブルを変更して、前のテーブルをアンロードします。

関数付きの奇妙なコードスニペットを使用し、unrequireコードにガベージコレクションを強制することで、なんとかそれを行うことができました。もっと文明的な方法でそれができるようになりたいです。WindowsとLinuxには関数dlcloseとがあります。FreeLibraryただし、ライブラリの実際のハンドルにアクセスできないため、それらを呼び出す方法はありません。

アンロード関数の欠如は、luaインタープリターがモジュールのC関数を使用しないかどうかを実際に知ることができないという事実によって動機付けられています。ライブラリを完全に削除するのではなく、ライブラリを更新するだけなので、これは私の状況には当てはまりません。

4

1 に答える 1

2

それを行う方法はありますが、それを機能させるには制限があります。

require基本的な考え方は、モジュールを使用するLua関数とスクリプト、および舞台裏で変更しようとしている実際のC関数の間に間接層を作成することです。これは、モジュール自体で行うのが最適です。つまり、Luaコードに関数などの置換を行わせるのではなく、C関数に自身の置換を実行させます。

そうすれば、あなたがコントロールできます。

アイデアは単純です。2つのモジュールがあります。Luaが関数を登録するためのスタブモジュールと、関数の実装を備えた実際のモジュールです。後者のモジュールのみが変更されます。

スタブモジュールは、Luaに登録する2つの機能を持つLuaモジュールです。1つ目はスタブ関数で、複数回登録されます。これは、単一のアップバリューを持つクロージャーとして登録されます。

そのアップバリューはC関数です。このスタブ関数が行う唯一のことは、Lua状態から1つのアップバリューを引き出し、それが与えられたのと同じパラメーターでそれを呼び出し、呼び出されている関数が返す値を正確に返すことです。パススルーを正しく行うには、スタックの微調整が少し必要ですが、それを処理する方法を知っていると思います。また、この関数はアップバリューの値をチェックする必要があります。その場合、nil何もせずに何も返しません(これによりエラーが防止されます)。

2番目の関数は、少し後で説明します。

スタブモジュール初期化ルーチンは、実際には単なるDLLである実際のモジュールをロードします。このDLLには、実際のモジュールがエクスポートする関数のリストを提供する関数が必要です。したがって、転送されるさまざまなLua関数を照会できます。

実際のモジュールからエクスポートされた関数ごとに、スタブ関数をLuaに登録し、その名前を付けます。スタブのアップバリューはエクスポートされた関数に設定され、アップバリューなしでロードされます。

明らかに、DLLへのハンドルは、おそらくレジストリエントリなどのユーザーデータとして保持する必要があります。

スタブモジュールがエクスポートする2番目の関数は、実際のモジュールをリロードする関数です。これを行うには、新しいDLLをロードします(古いDLLを最初にアンロードするのではありません)。このプロセスを堅牢にするには(関数リストが同一であることが保証されているにもかかわらず。このようなことを想定するのは嫌いです)、モジュールテーブルを反復処理する必要があります。

新しいDLLにない登録済み関数ごとに、そのupvalueをに設定しますnil(モジュールのテーブルからそれらを削除します)。新しいDLLに登録されているすべての関数について、upvalueを新しい関数に変更します。新しいDLLに未登録の関数がある場合は、スタブ関数を新しいupvalueに登録して、それらをテーブルに固定します。

これがすべて完了したら、古いDLLをアンロードできます。

三; 終わり。

注:これが機能するためには、モジュール関数が正常に動作している必要があります。他のC関数を登録するなどのことはできません。彼らは、Cの境界を越えてコルーチンを生成および再開するためにLua5.2のメカニズムを使用することはできません。などなど。そうしないと、アンロードされたDLLを呼び出すリスクがあります。

于 2012-08-09T10:10:54.043 に答える