【问题标题】:Nested structures with loadlibrary Matlab带有负载库 Matlab 的嵌套结构
【发布时间】:2015-03-12 14:48:55
【问题描述】:

我正在连接 Matlab 和 C 代码,以便能够直接使用 Matlab 中的一些 C 函数。我知道这些函数的原型,但里面的代码可能会改变。 为了接口这一切,我在 Matlab 中使用 loadlibrary 和 calllib,我不想使用 MEXFiles。

在 C 代码中,定义了一些结构。尽管如此,定义代码基本组件的那个可能会改变:它包含一些结构和变量,但是可以在这个中添加一些其他结构,我希望 Matlab 能够处理所有这些。

但是,正如 Mathworks 所说:

不支持嵌套结构或包含指向结构的指针的结构。但是,MATLAB 可以访问在外部库中创建的结构数组。

所以我不能在 Matlab 中直接存储嵌套结构。例如,主要成分是结构(a)。这个包含另一个结构(b)。并且 (b) 包含指向函数的指针。 如果我使用 libstruct 直接将 (a) 存储在变量中,当我在参数中调用带有 (a) 的 C 方法时,我们可以看到指针丢失了。最糟糕的是,C 代码知道指向结构 (b) 的指针是什么,但他无法访问指向的函数。 尽管如此,通过之前在 Matlab 中创建这个结构 (b),它可以工作,但它是特定于 (b) 的,我不能对其他嵌套结构做同样的事情。

这就是为什么我认为我必须阻止 Matlab 查找变量的类型。我只需要给它一个指向结构的指针并锁定所有与这个指针相关的东西,以便能够在带有 calllib 的 C 函数的参数中传递这个指针。

这就是我的问题:你知道我是否可以锁定包含结构 (a) 和所有相关内容的内存的一部分吗?你认为我可以阻止 Matlab 看这个指针是什么吗?

事实上,我只是想在一个 C 函数中创建一个嵌套结构并在另一个 C 函数中重新使用它,但是用 Matlab 调用这两个 C 函数(不使用 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) struct astruct b 和 c 函数的一些模拟代码以及您希望如何从 matlab 中调用它们会有所帮助。 2) 为什么要绝对避免让 mex 文件为您制作胶水并“按原样”处理 c-lib?
  • 1) 我添加了您要求的内容- :) 2) 我想避免使用 MexFiles,因为我加载的 DLL 具有我知道的特定原型的函数,但里面的内容可能会改变,并且我不想为每个 DLL 重新构建一些 MexFiles,而只是加载一个 DLL 就可以了! :)
  • 2) 现在更有意义 .. 即使作为替代或备份解决方案,您可以将 dll 名称作为参数传递给 mex 函数,并让它为您加载 mexCallMATLAB ...嗯...让我们阅读以获取更多详细信息;)

标签: matlab pointers structure loadlibrary


【解决方案1】:

从您的 cmets 中得知可以有不同的 dll,但导出的函数具有相同的原型,无论如何我会将库的管理委托给 mex 文件。

所以,请让我们考虑构建一个可以在 matlab 中使用的FmiManager.mex,如下所示:

[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') 并将回调的句柄作为输入 (只需在 c 代码中转换回 'struct b')
  • 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 端代码。一切都委托给 .mex 文件(即在 c 中)并作为句柄传递给 matlab。

【讨论】:

  • 感谢您的帮助! :) 我实现了你的建议,但我仍然无法获得指向回调函数的指针。为了打包组件并提供给 Matlab,我尝试了:plhs[0] = (mxArray*)(&amp;component);plhs[0] = (mxArray*)(component);,但它崩溃了。但我不确定我的 Matlab 代码是 component = fmiManager('INSTANTIATE', instanceName, 1, guid, resourceLocation, visible, loggingOn);。我也尝试使用该组件在 C 代码中创建一个全局变量,但是在通过 Matlab 之后我仍然无法访问函数指针。有任何想法吗? :)
  • @Kerial 要在 Matlab 和 C 之间编组 plhsprhs,请参阅以下 link1link2。例如,您可以在 matlab 中执行 plhs[0] = mxCreateDoubleScalar((int)ptrToComponent) 以将指针作为双精度返回;然后 ptrToComponent = (Component*)((int)mxGetScalar(prhs[0])) 从 matlab 中的 double 中回滚指针。
  • 抱歉转换失败了(现在无法测试)...但是plhs[0] = mxCreateDoubleScalar(ptrToComponent)ptrToComponent = (Component*)mxGetScalar(prhs[0]) 之类的应该没问题。
猜你喜欢
  • 1970-01-01
  • 2023-03-10
  • 2017-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多