6

私の質問は、matlab コンパイラとランタイムの謎に非常に固有のものです。matlab ランタイム API に詳しい人だけが答えるかもしれないので、多くの詳細を省略しました。もっと冗長にする必要があるかどうか教えてください。

序章

matlab コンパイラとランタイムを使用して、m コードで記述された関数を C# プログラムから呼び出すことができます。呼び出すとしましょう:

function [result] = foo(n)
%[
    result = 0;
    for k = 1:n,
        pause(1.0); % simulate long processing
        result = result + 42;
    end
%]

(C#コードのいくつかのdllimportの後ろのどこかに):

mclFeval(IntPtr inst, string name, IntPtr[] plhs, IntPtr[] prhs)

これまでのところ、問題はありません (つまり、ランタイムの初期化、「.cft」ファイルのロード、.Net 型を使用した MxArray の前後のマーシャリングなど...)。

私の問題

一部のコールバックfooを使用して、関数の進行状況を調査したいと思います。cancelprogress

function [result] = foo(n, cancelCB, progressCB)
%[
    if (nargin < 3), progressCB = @(ratio, msg) disp(sprintf('Ratio = %f, Msg = %s', ratio, msg)); end
    if (nargin < 2), cancelCB = @() disp('Checking cancel...'); end

    result = 0;
    for k = 1:n,

        if (~isempty(cancelCB)), 
            cancelCB(); % Up to the callback to raise some error('cancel');
        end;
        if (~isempty(progressCB)),  
           progressCB(k/n, sprintf('Processing (%i/%i)', k, n));
        end

        pause(1.0); % simulate long processing
        result = result + 42;
    end
%]

しかしもちろん、これらのコールバックを m-one 内ではなく、C# コード内に配置したいと考えています。

調査

  1. 「mclmcr.h」ヘッダー ファイルを見ると、次の関数が役立つようです。

    extern mxArray* mclCreateSimpleFunctionHandle(mxFunctionPtr fcn);
    extern bool mclRegisterExternalFunction(HMCRINSTANCE inst, const char* varname, mxFunctionPtr fcn);
    

    残念ながら、これらは完全に文書化されておらず、それらがどのように機能するかを理解するために模倣できるユースケースは見つかりませんでした.

  2. また、C# で COM 可視オブジェクトを作成し、それをパラメーターとして matlab コードに渡すことも考えました。

    // Somewhere within C# code:
    var survey = new ComSurvey();
    survey.SetCancelCallback =  () => { if (/**/) throw new OperationCancelException(); };
    survey.SetProgressCallback = (ratio, msg) => { /* do something */ };
    

     

    function [result] = foo(n, survey)
    %[
        if (nargin < 2), survey = []; end
    
        result = 0;
        for k = 1:n,
    
            if (~isempty(survey)),
               survey.CheckCancel(); % up to the COM object to raise exception
               survey.SetProgress(k/n, sprintf('Processing... %i/%i', k, n));
            end
    
            pause(1.0); % simulate long processing
            result = result + 42;
        end
    %]
    

    数値配列と構造配列を作成する関数に精通しており、それらの使用方法を知っています。

    extern mxArray *mxCreateNumericArray(...)
    extern mxArray *mxCreateStructArray(...)
    

    とにかく、COM オブジェクトが MxArray にどのようにパッケージ化されているかわかりません。

さらなる調査

日+1

まだ不安定な場合でも、matlab を C# コードにコールバックすることに成功しました。それmclCreateSimpleFunctionHandleが進むべき方向のようです。

注: 以下のコードは参考用です。そのままでは、独自のコンテキストに適していない場合があります。後で簡単なコードを提供します (つまり、安定したソリューションが得られたら)。

  1. の署名を見て、次のmxFunctionPtrような 2 つのデリゲートを作成しました。

    // Mimic low level signature for a Matlab function pointer
    [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    delegate void MCRInteropDelegate(int nlhs, IntPtr[] plhs, int nrhs, IntPtr[] prhs);
    

    // Same signature (but far more elegant from .NET perspective)
    delegate void MCRDelegate(MxArray[] varargouts, MxArray[] varargins);  
    
  2. また、次のようにランタイムにリンクしました。

    [DllImport("mclmcrrt74.dll", EntryPoint = "mclCreateSimpleFunctionHandle", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
    static extern IntPtr _mclCreateSimpleFunctionHandle(MCRInteropDelegate fctn);
    
  3. MxArrayハンドルを単純にカプセル化する私の .NET クラスであると仮定すると、mxArray*次のようにデリゲートをマーシャリングしました。

    // Create MxArray from corresponding .NET delegate
    static MxArray CreateFromDelegate(MCRDelegate del)
    {
        // Package high level delegate signature to a 'dllimport' signature
        MCRInteropDelegate interopDel = (nlhs, plhs, nrhs, prhs) =>
        {
            int k = 0;
    
            var varargouts = new MxArray[nlhs];
            var varargins = new MxArray[nrhs];
    
            // (nrhs, prhs) => MxArray[] varargins 
            Array.ForEach(varargins, x => new MxArray(prhs[k++], false)); // false = is to indicate that MxArray must not be disposed on .NET side
    
            // Call delegate
            del(varargouts, varargins); // Todo: varargouts created by the delegate must be destroyed by matlab, not by .NET !!
    
            // MxArray[] varargouts => (nlhs, plhs)
            k = 0;
            Array.ForEach(plhs, x => varargouts[k++].getPointer());
        };
    
        // Create the 1x1 array of 'function pointer' type
        return new MxArray(MCRInterop.mclCreateSimpleFunctionHandle(interopDel));
    }
    
  4. 最後に、が(再び、低レベルAPIにカプセル化する私のクラス)moduleのインスタンスであると仮定すると、関数を呼び出して、次のように .NETデリゲートを入力することができました。MCRModulehInst*mclFevalfoocancel

    // Create cancel callback in .NET
    MCRDelegate cancel = (varargouts, varargins) =>
    {
        if ((varargouts != null) && (varargouts.Length != 0) { throw new ArgumentException("'cancel' callback called with too many output arguments"); } 
        if ((varargins != null) && (varargins.Length != 0) { throw new ArgumentException("'cancel' callback called with too many input arguments"); }
    
        if (...mustCancel...) { throw new OperationCanceledException(); }
    }
    
    // Enter the m-code
    // NB: Below function automatically converts its parameters to MxArray
    // and then call low level mclFeval with correct 'mxArray*' handles
    module.Evaluate("foo", (double)10, cancel);
    

    この .NET コードは正常に機能し、デリゲートfooへのコールバックを適切に作成しました。cancel

    唯一の問題は、非常に不安定なことです。私の推測では、私はあまりにも多くの匿名関数を使用しており、おそらくそれらのいくつかはあまりにも早く破棄されています...

    今後数日以内に安定したソリューションを提供しようとします(できれば、すぐにテストできるように、独自のコンテキストで読み取りおよびコピーアンドペーストするためのより単純なコードを使用してください)。

    で間違った方向に進んでいると思われる場合はお知らせくださいmclCreateSimpleFunctionHandle

4

1 に答える 1

1

とった

mclCreateSimpleFunctionHandle事実上、関数ポインター(外部側)を保持する配列変数(matlab側)を作成するために呼び出すのに適切なAPI関数でした。これで、キャンセルと進行の目的でC#コードにコールバックするためにmコードをコンパイルすることができます。

の正しいマーシャリングについては、こちらmclCreateSimpleFunctionHandleで説明しています

于 2012-01-06T10:17:39.873 に答える