我如何创建一个cpp函数,可以使用函数作为参数从c#调用

时间:2016-01-25 11:29:51

标签: c# c++ function delegates c++-cli

正如问题一样。

我有一对库,一个在cpp中,另一个在c#中。我已经有几个(cpp)函数,我可以从c#调用,但只能使用简单的参数(数组和基元)。

我如何传递函数?

我已经在这里阅读了很多答案,而且没有一个能帮助我 - 我缺少什么?

以下是我所拥有的:

    delegate double ManagedFunction(cli::array<System::Double>^ xIn);
    ...

    ...
    static int LevMar(
        ManagedFunction^ function,
        cli::array<System::Double, 1>^ x0,
        cli::array<System::Double, 1>^ xHigh,
        cli::array<System::Double, 1>^ xLow,
        int maxIters//,
        //double tolerance
        ) {

        IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(function);
        Unmanaged::WrapperClass::functionToMinimize = static_cast<Unmanaged::UnmanagedFunction>(stubPointer.ToPointer());

        int m = x0->Length; //nDim
        int N = 1; //observables dimension

        double* fx = new double[m];

        double* x0Arr = Utility::CreateDoubleArrayFromManaged(x0);
        double* xLowArr = Utility::CreateDoubleArrayFromManaged(xLow);
        double* xHighArr = Utility::CreateDoubleArrayFromManaged(xHigh);

        double info[LM_INFO_SZ];


        double tolerance[5];
        tolerance[0] = 0.01; //scale factor for initial \mu/ 
        tolerance[1] = tolerance[2] = tolerance[3] = tolerance[4] = 1.0e-5;

        dlevmar_bc_dif(Unmanaged::WrapperClass::functionToPassToLevMar, x0Arr, fx, m, N, xLowArr, xHighArr, NULL, maxIters, tolerance, info, NULL, NULL, NULL);
        int nElements = sizeof x0Arr / sizeof x0Arr[0];
        for (int i = 0; i < nElements; i++) x0[i] = x0Arr[0];
        return 0;
    }

这一切都是编译,但我还没有测试过,因为我无法从c#中调用它;这就是我尝试过的:

CostDelegate costDelegate = new CostDelegate(cost);
XXXCpp.Minimization.LevMar(costDelegate, x0, xhigh, xlow, 1000);

但是它告诉我它不能来自CostDelegate&#39;到管理功能&#39;。两者都是代表,两者都有相同的签名。

我如何实现我在这里所做的事情?

有没有办法做到这一点,以便在c#中,当我调用函数时,我可以传递一个函数,而不必创建一个委托(即我可以编写cpp使得该函数采用{{ 1}}而不是这个&#39; ManagedFunction&#39;对象)?

2 个答案:

答案 0 :(得分:1)

delegate double ManagedFunction(cli::array<System::Double>^ xIn);

您忘了宣布public。必需,两个委托类型永远不兼容,即使它们的签名相同也是如此。您的C#代码必须创建一个ManagedFunction委托,它只能在您公开时才这样做。请考虑使用Action<array<double>^>^,但请阅读下一个项目符号。

您需要注意的其他一些怪癖:

  • 注意本机代码在函数指针上假定的调用约定。默认值为__cdecl,但您的委托仅与__stdcall兼容。如果它不是__stdcall,则必须应用[UnmanagedFunctionPointer]属性来声明调用约定。

  • stubPointer 变量的一个非常棘手的问题是它在进行本机函数调用之前可能会变为无效。当程序中的其他线程运行托管代码并且其中一个线程触发垃圾收集时,很少会发生这种情况。你必须写GC :: KeepAlive(函数);在函数调用之后,确保这不会出错,这会延长委托对象的报告生命周期。

  • 复制数组效率低下,没有必要。您可以使用pin_ptr<double>来避免这种情况。这会生成一个指向数组第一个元素的指针,并在函数体的生命周期内将其固定。本机代码直接从GC堆中寻址数组元素。

答案 1 :(得分:0)