5

C#/CIL/Reflection.Emit 質問:

関数ポインターを持つ型を定義し、その型をインスタンス化し、別の型で静的メソッドを作成しようとしています (「単なる関数」を作成する他の方法がわからないため)、この静的メソッドへのポインターを与えるインスタンスに、後でそのポインタを使用して関数を呼び出します。

私はほとんど成功していません。:-(

タイプは次のとおりです。

Thunk = modb.DefineType("Thunk");

Thunk.DefineField("Env" , Env.AsType(), FieldAttributes.Public);
Thunk.DefineField("Expr", typeof(int), FieldAttributes.Public); // int is the correct type according to http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldftn.aspx

Thunk.CreateType();

ここまでは順調です(と思います)。次に、これらのいずれかを作成し、関数ポインターを割り当てます。

var methodBuilder = MainType.DefineMethod("my_other_little_function", MethodAttributes.Static, typeof(Int64), new[] {Env.AsType()});
{
    var il2 = methodBuilder.GetILGenerator();
    il2.Emit(OpCodes.Ldarg_0);
    il2.Emit(OpCodes.Stloc_0);
    binding.Expr.Compile(il2);
    il2.Emit(OpCodes.Ret);
}
il.Emit(OpCodes.Newobj, ThunkCtor);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Stfld, Thunk.GetField("Env"));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldftn, methodBuilder);
il.Emit(OpCodes.Stfld, Thunk.GetField("Expr"));

私が知る限り、この部分は問題なく機能しています。私がそれを呼び出そうとすると、問題は別の場所にあります:

var func = il.DeclareLocal(typeof(int));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldfld, Thunk.GetField("Expr"));
il.Emit(OpCodes.Stloc, func);
il.Emit(OpCodes.Ldfld, Thunk.GetField("Env"));
il.Emit(OpCodes.Ldloc, func);

il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(Int64), new[] { Env.AsType() }, null);

これによって作成されたプログラムを実行しようとすると、バイトコードが実行される前にエラーが発生します (または、少なくともそのように表示されます)。

Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program.

代わりに、EmitCalli() を単純に関数ポインターと引数をポップして数値をプッシュするコードに置き換えると、プログラムの残りの部分は正常に動作します。では、この関数をどのように呼び出す必要がありますか?

本当にありがとう。:-)

4

2 に答える 2

1

このような問題を楽しみのために解決するのが好きでない限り、別のオプションは、ライブラリを使用して目的の IL を生成することです。

Fasterflectは、多くのリフレクション ヘルパーを備えたライブラリです。たとえば、メソッドのデリゲートを取得するには、次のように記述します。

var delegate = typeof(YourClass).DelegateForCallStaticMethod( "MyStaticMethod" );
delegate( args );

このライブラリは、バックグラウンドで DynamicMethod と IL 生成を使用し、デリゲートの変数を宣言して使用する必要のない拡張機能もあります (ただし、タイトなループなどで繰り返し呼び出す場合は、はるかに高速になります)。 . 単純なバリアントは次のようになります。

var result = typeof(YourClass).CallStaticMethod( "MyStaticMethod", args );

Fasterflect は、生成されたデリゲートをキャッシュします (生成された IL をコンパイルするには非常にコストがかかるため)。そのため、単純なシナリオでの呼び出しごとに追加されるパフォーマンス コストは、キャッシュ ルックアップを実行するコストと同じになります。

免責事項: 私は上記のプロジェクトに関与しています。とはいえ、IL を手で書くのは本当に楽しい作業ではありません。IL 生成のもう 1 つの適切なオプションは、Mono.Cecil ライブラリですが、それが提供するものの詳細については詳しくありません。

于 2012-12-23T03:23:14.760 に答える
1

IntPtrの代わりに for 関数ポインタを使用する必要がありintます。CLR のドキュメントでは、この型を「ネイティブ int」と呼ぶことがあります。

于 2016-09-14T06:44:38.667 に答える