Вложенные структуры с loadlibrary Matlab
Я взаимодействую с Matlab и C-кодом, чтобы иметь возможность напрямую использовать некоторые C-функции в Matlab. Я знаю прототип этих функций, но код внутри может измениться. Для того, чтобы взаимодействовать со всем этим, я использую loadlibrary и calllib в Matlab, и я не хочу использовать MEXFiles.
В C-коде определены некоторые структуры. Тем не менее, тот, который определяет базовый компонент кода, может измениться : он содержит некоторую структуру и переменные, но некоторые другие структуры могут быть добавлено в это, и я хотел бы, чтобы Matlab был в состоянии справиться со всем этим.
Но, как говорит Mathworks:
Вложенные структуры или структуры, содержащие указатель на структуру, не поддерживаются. Однако MATLAB может получить доступ к массиву структур, созданных во внешней библиотеке.
Поэтому я не могу хранить непосредственно вложенную структуру в Matlab. Например, главным компонентом является структура (а). Это одно содержит другую структуру (b). И (b) содержит указатель на функцию. Если я непосредственно сохраняю (a) в переменной с помощью libstruct, то при вызове C-метода с (a) в аргументе мы можем видеть, что указатели потеряны. Хуже всего то, что C-код знает, что такое указатель на структуру (b), но он не может получить доступ к указанным функциям. Тем не менее, создав эту структуру (b) раньше в Matlab, она работает, но она специфична для (b), и я не могу сделать то же самое для других вложенных структур.
Вот почему я думал, что должен предотвратить Matlab от поиска типа переменной. Я просто должен дать ему указатель на структуру и заблокировать все, что связано с этим указателем, чтобы иметь возможность передать этот указатель в аргументе C-функции с calllib.
Итак, вот мой вопрос : Знаете ли вы, Могу ли я заблокировать часть памяти, которая содержит структуру (а) и все, что связано с ней? И вы думаете, что я могу помешать Matlab посмотреть, что это за указатель?На самом деле, я просто хочу создать вложенная структура в C-функции и повторно использовать ее в другой C-функции, но вызывать эти две C-функции с помощью Matlab (без использования MexFiles).
Спасибо! :)
C-код
Структура (а)
typedef struct {
fmi2Real *r;
fmi2Integer *i;
fmi2Boolean *b;
fmi2String *s;
fmi2Boolean *isPositive;
fmi2Real time;
fmi2String instanceName;
fmi2Type type;
fmi2String GUID;
const fmi2CallbackFunctions *functions;
fmi2Boolean loggingOn;
fmi2Boolean logCategories[NUMBER_OF_CATEGORIES];
fmi2ComponentEnvironment componentEnvironment;
ModelState state;
fmi2EventInfo eventInfo;
int isDirtyValues; // !0 is true
} ModelInstance;
Другая структура может быть добавлена в эту структуру (a).
Структура (b)
typedef struct {
const fmi2CallbackLogger logger;
const fmi2CallbackAllocateMemory allocateMemory;
const fmi2CallbackFreeMemory freeMemory;
const fmi2StepFinished stepFinished;
const fmi2ComponentEnvironment componentEnvironment;
} fmi2CallbackFunctions;
typedef void (*fmi2CallbackLogger) (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String, ...);
typedef void* (*fmi2CallbackAllocateMemory)(size_t, size_t);
typedef void (*fmi2CallbackFreeMemory) (void*);
typedef void (*fmi2StepFinished) (fmi2ComponentEnvironment, fmi2Status);
Прототип одной из C-функций (первая из которых создает основную компонент)
fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2String fmuGUID,
fmi2String fmuResourceLocation, const fmi2CallbackFunctions *functions,
fmi2Boolean visible, fmi2Boolean loggingOn);
typedef void* fmi2Component;
код Matlab
Вызовите функцию fmi2Instantiate и создайте компонент.
fmu.component=calllib(model, 'fmi2Instantiate', libpointer('int8Ptr', fmu.instanceName), fmu.type, libpointer('int8Ptr', fmu.guid), libpointer('int8Ptr', resourceLocation), fmu.callbackFunctions, visible, loggingOn);
Этот компонент будет далее передан в аргументе другой C-функции.
1 ответ:
Читая из ваших комментариев, что могут быть разные библиотеки DLL, но с экспортированными функциями, имеющими одинаковые прототипы, я бы в любом случае делегировал управление библиотекой файлу mex.
Итак, давайте рассмотрим построение
FmiManager.mex
, которое можно использовать из matlab следующим образом:[ok, msg] = FmiManager('SETUP', libraryName); [hCallbacks] = FmiManager('CREATE_CALLBACKS', ... params for creating callbacks ...); [hInstance] = FmiManager('CREATE_INSTANCE', hCallbacks, ... other params ...) [...] = FmiManager('OTHER_COMMAND', ...);
Это довольно просто, первый параметр - это то, что вы хотите сделать с библиотекой, а остальные параметры относятся к функции, которую вы хотите вызвать в c-коде.
SETUP
является дополнительная команда для динамического изменения библиотеки, с которой вы связаны.CREATE_CALLBACKS
вызывает c-функцию для создания обратных вызовов и возвращает им дескриптор (например, просто приведя c-указатель к 'struct b' как int)CREATE_INSTANCE
вызывает c-функцию для создания экземпляра главного компонента и возвращает дескриптор к созданному экземпляру (опять простое приведение к int для 'struct a', например) и принимает в качестве входных данных дескриптор к обратному вызову (просто нужно привести обратно к 'struct b' в c-коде)OTHER_COMMAND
при необходимости можно расширить процесс на другие c-функции ...Вот тогда некоторый псевдокод, показывающий, как построить этот
FmiManager.mex
файл:Я надеюсь, что вам понравится это альтернативное решение. Главное его преимущество заключается в том, что вам не нужно беспокоиться о том, чтобы структуры, созданные в matlab, соответствовали c-side коду. Все делегируется своим .mex файл (то есть в c) и передается в matlab в качестве дескрипторов.[FmiManager.c] #include <mex.h> #include <windows.h> #include "FmiManager.h" // The header for this mex file #include "fmi.h" // The header for types in your 'fmi' library // Pointer to correct dll static HINSTANCE hDll = NULL; // Pointer to the function that creates the callbacks typedef fmi2CallbackFunctions* (FMI_CALLCONV createCallBacksPtr *)(...) static createCallBacksPtr createCallBacks = NULL; // Pointer to the function that performs instantiation typedef fmi2Component* (FMI_CALLCONV instanciatePtr *)(...) static instanciatePtr instanciate = NULL; // Mex entry point void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { // Arguments parsing if (nrhs < 1) { mexErrMsgTxt("Not enough input arguments."); return; } if (!mxIsChar(prhs[0])) { mexErrMsgTxt("First parameter must be a string."); return; } // Command selection if (commandIs(prhs[0], "SETUP")) { processSetup(nlhs, plhs, nrhs, prhs); } else if (commandIs(prhs[0], "CREATE_CALLBACKS")) { processCreateCallbacks(nlhs, plhs, nrhs, prhs); } else if (commandIs(prhs[0], "INSTANCIATE")) { processInstanciate(nlhs, plhs, nrhs, prhs); } else { mexErrMsgTxt("Unknown command or command not implemented yet."); } } // Processing 'SETUP' command void processSetup(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { // Free previous library if (hDll != NULL) { FreeLibrary(hDll); hDll = NULL; } // Load the new one char* librayPath = getThisMexPath(); ... load library from '*prhs' (i.e. hDll = loadLibrary(fullpath))... mxFree(librayPath); // Bind functions pointers createCallBacks = (createCallBacksPtr)GetProcAddress(hDll, "createCallbacks"); if (createCallBacks == NULL) { mexErrMsgTxt("Failed to map 'createCallBacks'"); return; } instanciate = (instanciatePtr)GetProcAddress(hDll, "instanciate"); if (instanciate == NULL) { mexErrMsgTxt("Failed to map 'instanciate'"); return; } } // Processing 'CREATE_CALLBACKS' command void processCreateCallbacks(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { ... unpack '*prhs' ... int hCallbacks = (int)createCallback(...); // Function has been binded during setup ... pack 'hCallbacks' into '*plhs' ... } // Processing 'INSTANCIATE' command void processInstanciate(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { ... unpack '*prhs' ... int hInstance = (int)instanciate(...); // Function has been binded during setup ... pack 'hInstance' into '*plhs' ... } // Check if some command is really some givent one bool commandIs(const mxArray* mxCommand, const char* command) { double result; mxArray* plhs1[1]; mxArray* prhs1[1]; mxArray* plhs2[1]; mxArray* prhs2[2]; if (mxCommand == NULL) { mexErrMsgTxt("'mxCommand' is null"); return false; } if (command == NULL) { mexErrMsgTxt("'command' is null"); return false; } if (!mxIsChar(mxCommand)) { mexErrMsgTxt("'mxCommand' is not a string"); return false; } // First trim prhs1[0] = (mxArray*)mxCommand; mexCallMATLAB(1, plhs1, 1, prhs1, "strtrim"); // Then compare prhs2[0] = mxCreateString(command); prhs2[1] = plhs1[0]; mexCallMATLAB(1, plhs2, 2, prhs2, "strcmpi"); // Return comparison result result = mxGetScalar(plhs2[0]); return (result != 0.0); } // Obtain the path of current mex file // CAREFUL: Use mxFree on return pointer !! char* getThisMexPath() { mxArray *rhs[1], *lhs[1]; char *path, *name; size_t lenpath, lenname, n; rhs[0] = mxCreateString("fullpath"); mexCallMATLAB(1, lhs, 1, rhs, "mfilename"); mxDestroyArray(rhs[0]); path = mxArrayToString(lhs[0]); mxDestroyArray(lhs[0]); mexCallMATLAB(1, lhs, 0, rhs, "mfilename"); name = mxArrayToString(lhs[0]); mxDestroyArray(lhs[0]); lenpath = strlen(path); lenname = strlen(name); n = lenpath - lenname; path[n] = '\0'; mxFree(name); return path; // Don't forget mxFree !!! }