2011-10-25 12 views
5

Mi pregunta es extremadamente específica para los arcanes del compilador de matlab y el tiempo de ejecución. Como solo las personas que están familiarizadas con la API de tiempo de ejecución de matlab pueden responder, acorté muchos detalles. Por favor, avíseme si debería ser más detallado.Matlab: ¿Cómo estudiar la progresión compilada del código m desde una API externa?

Introducción

Usando el compilador MATLAB & tiempo de ejecución que puedo llamar a una función escrita en m-código de un programa en C#. Digamos que llamar:

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

con (en algún lugar detrás de unos dllimports en el código C#):

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

Hasta ahora, todo va bien, no tengo ningún problema con esto (es decir intializing el tiempo de ejecución, la carga de la archivo '.cft', cálculo de referencias de ida y vuelta con MxArray tipos de .NET, etc ...)

mi problema

me gustaría estudiar la progresión de mi función u foo cantar algunas cancel y progress devoluciones de llamada:

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

Pero por supuesto me gustaría estas devoluciones de llamada para estar en el código C#, no dentro de la m-uno.

Investigaciones

  1. Mirando archivo de cabecera '' mclmcr.h, parece que estas funciones pueden ser de ayuda:

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

    Desafortunadamente estos son totalmente indocumentado y no encontraron casos de uso Podría imitar para entender cómo funcionan.

  2. también he pensado en crear un objeto visible COM en C# y pasarlo como un parámetro para el código de 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 
    %] 
    

    Estoy muy familiarizado con funciones para crear matrices numéricas y de estructura y saber cómo usarlas:

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

    De todos modos, ¿cómo se empaquetan los objetos COM en MxArrays, no lo sé?

Otras investigaciones

Día + 1

Aunque todavía inestable, he conseguido tener MATLAB para callback en mi código C# y parece que mclCreateSimpleFunctionHandle es la dirección a seguir.

Nota: El código abajo es solo de referencia. Puede no ser adecuado en su propio contexto como es. Proporcionaré un código más simple más adelante (es decir, una vez que obtenga una solución estable).

  1. Mirando a la firma del mxFunctionPtr, he creado dos delegados como este:

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

    y

    // Same signature (but far more elegant from .NET perspective) 
    delegate void MCRDelegate(MxArray[] varargouts, MxArray[] varargins); 
    
  2. también vinculado a la ejecución como esto:

    [DllImport("mclmcrrt74.dll", EntryPoint = "mclCreateSimpleFunctionHandle", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)] 
    static extern IntPtr _mclCreateSimpleFunctionHandle(MCRInteropDelegate fctn); 
    
  3. Suponiendo MxArray es una clase .NET de faena que encapsulan para mxArray* mangos, que luego calcular las referencias mis delegados como este:

    // 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. Por último, en el supuesto module es una instancia de MCRModule (de nuevo, una clase de mina para encapsular hInst* de bajo nivel mclFeval API), yo era capaz de llamar a la función foo y hacer que entre en mi .NET cancel delegado de la siguiente manera:

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

    Este código .NET funcionó bien, y foo realmente hizo una devolución de llamada al delegado cancel correctamente.

    El único problema es que es bastante inestable. Supongo que utilicé demasiadas funciones anónimas, y probablemente algunas de ellas se eliminen demasiado pronto ...

    Trataré de proporcionar una solución estable en los próximos días (con suerte, con un código más simple para leer y copiar -pasar en su propio contexto para pruebas inmediatas).

    Háganme saber si cree que voy en la dirección incorrecta con mclCreateSimpleFunctionHandle.

+0

¿Ha considerado el enfoque ingenuo de romper la acción en trozos pequeños y ejecutarlos en un bucle en C#? –

+0

Sí, lo pensé (también para proporcionar una aplicación de muestra aquí sin tener todo el código detrás que agregué para trabajar con el MCR). Probablemente regrese sobre el tema a principios del próximo año, por ahora estoy demasiado inscrito en otros temas no relacionados (WPF). – CitizenInsane

+0

Ok, @CitizenInsane, por favor actualiza esta publicación si puedes, ¡realmente necesito esas cosas! –

Respuesta

1

Tienes que

mclCreateSimpleFunctionHandle era efectivamente la función API derecho de llamar a fin de crear una variable de matriz (por parte de Matlab) de retención para un puntero de función (en el lado del externo). Ahora puedo compilar m-code para volver a llamar a mi código C# para cancelación y progresión.

de clasificación correcta para mclCreateSimpleFunctionHandle se describe here

Cuestiones relacionadas