Вложенные структуры с 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 2

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 файл:

[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 !!! 
}
Я надеюсь, что вам понравится это альтернативное решение. Главное его преимущество заключается в том, что вам не нужно беспокоиться о том, чтобы структуры, созданные в matlab, соответствовали c-side коду. Все делегируется своим .mex файл (то есть в c) и передается в matlab в качестве дескрипторов.