(替代标题:如何在C或C ++中实现等效于CLR委托的方法]
考虑此C函数:
int Test(int(*fn1)(double a));
如果要从C程序调用此函数,则无法将任意状态对象与函数指针一起传递-我只能有效地使用全局状态。这是一个常见问题,这就是为什么许多C API提供类似于[]
int Test(int(*fn1)(double a, void *state), void *state);
但是,令我惊讶的是,我注意到从C#程序中调用函数的第一个版本时这不是问题。
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int CallbackType(double something); [DllImport("TestLib.dll", CallingConvention = CallingConvention.Cdecl)] extern static int Test(CallbackType fn);
[当回调函数被调用时(即在C#代码中),
this
指针及其所有成员都被保留(这自动意味着可以使闭包和多播委托等附加功能易于使用)。
我不理解封送拆收器如何将2个指针的信息压缩为1。我做了很多测试,并意识到在使用TestCallback
的不同实例(即C#中的不同调用目标)调用C函数时,每次到达C的函数指针都是一个不同的地址。更准确地说,似乎TestCallback
实例与唯一的C函数指针地址之间存在直接1:1映射,并且该地址似乎是持久的-但是我无法找到该地址在TestCallback
内部存储的位置]实例。
我的结论是,在程序运行时实例化TestCallback
时,CLR必须向RAM中发出可执行的本机代码块。该代码块使用硬编码的状态对象指针来调用调度程序功能(状态对象可能是为其发出黑色代码的特定TestCallback
实例)。
但是,到目前为止,我还没有发现任何可以证实或反对的东西-对此主题没有深入的信息,或者被肤浅的教程所掩盖。
如果是这样,那么在程序存储器和数据存储器严格分开的体系结构上如何使CPU无法从数据存储器加载运行时生成的代码,那怎么可能?它在要求提前编译的平台上如何工作?怎样在较低级别的语言(如C或C ++)中实现类似的功能?
我用于测试的一些其他代码:
C header file ===================
extern __declspec(dllexport) int Test(
int(*fn1)(double a), int *address1,
int(*fn2)(double a), int *address2,
int(*fn3)(double a), int *address3
);
C file ===================
int Test(
int(*fn1)(double a), int *address1,
int(*fn2)(double a), int *address2,
int(*fn3)(double a), int *address3
)
{
*address1 = (int)fn1;
*address2 = (int)fn2;
*address3 = (int)fn3;
int result = fn1(5538867.0);
result += 9;
return result;
}
C# file ===================
class Program
{
static void Main(string[] args)
{
var sc1 = new SomeClass(5538867);
var sc2 = new SomeClass(-999999);
var callback1 = new CallbackType(sc1.TheCallback);
var callback2 = new CallbackType(sc2.TheCallback);
int called_address1 = 0, called_address2 = 0, called_address3 = 0;
var result = Test(
callback1, ref called_address1,
callback2, ref called_address2,
callback2, ref called_address3
);
// should be 9 or 8
Console.WriteLine(result);
Console.WriteLine(called_address1.ToString("x8"));
Console.WriteLine(called_address2.ToString("x8"));
Console.WriteLine(called_address3.ToString("x8"));
result = Test(
callback1, ref called_address1,
callback2, ref called_address2,
callback2, ref called_address3
);
Console.WriteLine(result);
Console.WriteLine(called_address1.ToString("x8"));
Console.WriteLine(called_address2.ToString("x8"));
Console.WriteLine(called_address3.ToString("x8"));
GC.KeepAlive(callback1);
GC.KeepAlive(callback2);
Console.ReadKey();
}
class SomeClass
{
public SomeClass(int i)
{
this.i = i;
this.something_else = "sjdklfjksdf";
}
private readonly int i;
private readonly string something_else;
public int TheCallback(double something)
{
return (int)something - this.i + this.something_else.Length - 11;
}
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CallbackType(double something);
[DllImport("TestLib.dll", CallingConvention = CallingConvention.Cdecl)]
extern static int Test(
CallbackType callback1, ref int called_address1,
CallbackType callback2, ref int called_address2,
CallbackType callback3, ref int called_address3
);
}
((替代标题:如何在C或C ++中实现等效于CLR委托的方法。)考虑以下C函数:int Test(int(* fn1)(double a));如果要从C程序调用此函数,我会...
此后我找到了部分答案。