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
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.
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).
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);
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);
Suponiendo
MxArray
es una clase .NET de faena que encapsulan paramxArray*
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)); }
Por último, en el supuesto
module
es una instancia deMCRModule
(de nuevo, una clase de mina para encapsularhInst*
de bajo nivelmclFeval
API), yo era capaz de llamar a la funciónfoo
y hacer que entre en mi .NETcancel
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 delegadocancel
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
.
¿Ha considerado el enfoque ingenuo de romper la acción en trozos pequeños y ejecutarlos en un bucle en C#? –
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
Ok, @CitizenInsane, por favor actualiza esta publicación si puedes, ¡realmente necesito esas cosas! –