それを行う方法はありますが、それを機能させるには制限があります。
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を呼び出すリスクがあります。