2011-10-25 14 views
5

Moje pytanie jest bardzo specyficzne dla wątków kompilatora i środowiska wykonawczego matlab. Ponieważ tylko osoby znające API środowiska wykonawczego matlab mogą odpowiedzieć, znacznie skróciłem szczegóły. Daj mi znać, jeśli mam być bardziej szczegółowy.Matlab: Jak badać skompilowany postęp m-code z zewnętrznego API?

Wprowadzenie

Korzystanie MATLAB kompilatora & czas pracy mogę wywołać funkcję napisane w m-kodu z programu C#. Powiedzmy, nazywając:

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

z (gdzieś za kilka dllimports w kodzie C#):

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

Tak daleko, tak dobrze, nie mam problemu z tym (tj intializing runtime, ładowanie '.cft' file, zestawiania iz powrotem MxArray z Net typów, etc ...)

Mój problem

chciałbym przyjrzeć się postępowi moim foo funkcji u śpiewać jakieś cancel i progress zwrotnych:

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 
%] 

Ale oczywiście chciałbym te wywołania zwrotne, aby być w kodzie C#, a nie w m-jeden.

Badania

  1. Patrząc na nagłówku pliku „mclmcr.h”, wygląda jak te funkcje mogą być pomocne:

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

    Niestety są one całkowicie nieudokumentowane i nie znalazłem przypadek użycia Mógłbym naśladować, aby zrozumieć, jak działają.

  2. Ja również myślał o tworzeniu COM widocznego obiektu w C# i przekazać go jako parametr do kodu 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 
    %] 
    

    jestem bardzo obeznany z funkcje do tworzenia tablic numerycznych i struktury oraz ich stosowania:

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

    W każdym razie, jak obiekty COM są pakowane do MxArrays, nie wiem?

Dalsze badania

Dzień + 1

Nawet jeśli nadal niestabilna, udało mi się mieć Matlab do callback do mojego kodu C# i wydaje się, że mclCreateSimpleFunctionHandle jest kierunek iść.

Uwaga: Poniższy kod służy wyłącznie jako odniesienie. Może nie być odpowiedni w twoim własnym kontekście, jaki jest. Dostarczę prostszy kod później (tj. Gdy dostanę stabilne rozwiązanie).

  1. Patrząc podpisania mxFunctionPtr, stworzyłem dwóch delegatów tak:

    // 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); 
    

    i

    // Same signature (but far more elegant from .NET perspective) 
    delegate void MCRDelegate(MxArray[] varargouts, MxArray[] varargins); 
    
  2. ja również związane z wykonywania tak:

    [DllImport("mclmcrrt74.dll", EntryPoint = "mclCreateSimpleFunctionHandle", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] 
    static extern IntPtr _mclCreateSimpleFunctionHandle(MCRInteropDelegate fctn); 
    
  3. Zakładając MxArray jest klasą .NET z kopalni, które po prostu hermetyzacji dla mxArray* uchwytami, ja wtedy marshaled moje delegatów tak:

    // 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. Wreszcie, zakładając module jest instancją MCRModule (ponownie, klasa mine do hermetyzacji hInst* na niskim poziomie mclFeval API), udało mi się zadzwonić foo funkcję i mieć to, aby wprowadzić moją .NET cancel delegat tak:

    // 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); 
    

    Ten kod .NET działał bez zarzutu, a foo rzeczywiście poprawnie wykonał oddzwanianie do delegata cancel.

    Jedynym problemem jest to, że jest dość niestabilny. Domyślam się, że użyłem zbyt wielu anonimowych funkcji i prawdopodobnie niektóre z nich są zbyt wcześnie ...

    Czy spróbuję dostarczyć stabilne rozwiązanie w ciągu kilku następnych dni? (mam nadzieję, że dzięki prostszemu kodowi do odczytu i kopiowania - wklej w swoim własnym kontekście do natychmiastowego przetestowania).

    Proszę dać mi znać, jeśli myślisz, że idę w złym kierunku z mclCreateSimpleFunctionHandle.

+0

Czy za naiwne podejście łamanie działania na małe kawałki i uruchomić je w pętli w C#? –

+0

Tak, ale o tym (również w celu dostarczenia przykładowej aplikacji tutaj bez całego kodu za dodałem do pracy z MCR). Prawdopodobnie wrócę do tej kwestii na początku przyszłego roku, bo teraz jestem zbyt zapisany do innych niezwiązanych problemów (WPF). – CitizenInsane

+0

OK, @CitizenInsane, proszę zaktualizować ten post, jeśli możesz, naprawdę potrzebuję tego! –

Odpowiedz

1

Got to

mclCreateSimpleFunctionHandle było skutecznie prawo funkcja API zadzwonić na aby stworzyć zmienną tablicową (na stronie Matlaba) trzymając za wskaźnik funkcji (na zewnętrznej jego stronie). Mogę teraz skompilować kod m, aby oddzwonić do mojego kodu C# w celach anulowania i postępu.

Prawidłowe marshalling dla mclCreateSimpleFunctionHandle opisano here

Powiązane problemy