带有 loadlibrary Matlab 的嵌套结构
Nested structures with loadlibrary Matlab
我正在连接 Matlab 和 C 代码,以便能够在 Matlab 中直接使用一些 C 函数。我知道这些函数的原型,但里面的代码可能会改变。
为了将它们连接起来,我在 Matlab 中使用了 loadlibrary 和 calllib,我不想使用 MEXFiles。
在 C 代码中,定义了一些结构。尽管如此,定义代码基本组件的那个可能会改变:它包含一些结构和变量,但是可以在这个中添加一些其他结构,我希望 Matlab 能够处理所有这些。
但是,正如 Mathworks 所说:
Nested structures or structures containing a pointer to a structure are not supported. However, MATLAB can access an array of structures created in an external library.
所以我不能在 Matlab 中直接存储嵌套结构。例如,主要成分是结构(a)。这个包含另一个结构(b)。并且 (b) 包含指向函数的指针。
如果我使用 libstruct 直接将 (a) 存储在变量中,当我在参数中调用带有 (a) 的 C 方法时,我们可以看到指针丢失了。最糟糕的是,C 代码知道指向结构 (b) 的指针是什么,但他无法访问指向的函数。
尽管如此,通过在 Matlab 中创建这个结构 (b) 之前,它可以工作,但它是特定于 (b) 的,我不能对其他嵌套结构做同样的事情。
这就是为什么我认为我必须阻止 Matlab 查看变量类型的原因。我只需要给它指向结构的指针并锁定所有与此指针相关的内容,以便能够将此指针传递给带有 calllib 的 C 函数的参数。
所以这就是我的问题:你知道我是否可以锁定包含结构 (a) 的内存的一部分以及与之相关的所有内容吗?你认为我可以阻止 Matlab 查看这个指针是什么吗?
事实上,我只是想在 C 函数中创建一个嵌套结构并在另一个 C 函数中重新使用它,但是用 Matlab 调用这两个 C 函数(不使用 MexFiles)。
谢谢! :)
C-code
结构(一)
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 函数的参数。
从您的评论中了解到可以有不同的 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 函数来创建回调和 returns 它们的句柄 (例如,通过简单地将 c 指针转换为 'struct b' 作为整数)
CREATE_INSTANCE
调用 c 函数来实例化主要组件和 returns 处理创建的实例 (再次简单地转换为 int for '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] = '[=11=]';
mxFree(name);
return path; // Don't forget mxFree !!!
}
希望您会喜欢这个替代解决方案。它的主要优点是您不必费心确保在 matlab 中创建的结构适合 c 端代码。一切都委托给 .mex 文件(即在 c 中)并作为句柄传递给 matlab。
我正在连接 Matlab 和 C 代码,以便能够在 Matlab 中直接使用一些 C 函数。我知道这些函数的原型,但里面的代码可能会改变。 为了将它们连接起来,我在 Matlab 中使用了 loadlibrary 和 calllib,我不想使用 MEXFiles。
在 C 代码中,定义了一些结构。尽管如此,定义代码基本组件的那个可能会改变:它包含一些结构和变量,但是可以在这个中添加一些其他结构,我希望 Matlab 能够处理所有这些。
但是,正如 Mathworks 所说:
Nested structures or structures containing a pointer to a structure are not supported. However, MATLAB can access an array of structures created in an external library.
所以我不能在 Matlab 中直接存储嵌套结构。例如,主要成分是结构(a)。这个包含另一个结构(b)。并且 (b) 包含指向函数的指针。 如果我使用 libstruct 直接将 (a) 存储在变量中,当我在参数中调用带有 (a) 的 C 方法时,我们可以看到指针丢失了。最糟糕的是,C 代码知道指向结构 (b) 的指针是什么,但他无法访问指向的函数。 尽管如此,通过在 Matlab 中创建这个结构 (b) 之前,它可以工作,但它是特定于 (b) 的,我不能对其他嵌套结构做同样的事情。
这就是为什么我认为我必须阻止 Matlab 查看变量类型的原因。我只需要给它指向结构的指针并锁定所有与此指针相关的内容,以便能够将此指针传递给带有 calllib 的 C 函数的参数。
所以这就是我的问题:你知道我是否可以锁定包含结构 (a) 的内存的一部分以及与之相关的所有内容吗?你认为我可以阻止 Matlab 查看这个指针是什么吗?
事实上,我只是想在 C 函数中创建一个嵌套结构并在另一个 C 函数中重新使用它,但是用 Matlab 调用这两个 C 函数(不使用 MexFiles)。
谢谢! :)
C-code
结构(一)
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 函数的参数。
从您的评论中了解到可以有不同的 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 函数来创建回调和 returns 它们的句柄 (例如,通过简单地将 c 指针转换为 'struct b' 作为整数)CREATE_INSTANCE
调用 c 函数来实例化主要组件和 returns 处理创建的实例 (再次简单地转换为 int for '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] = '[=11=]';
mxFree(name);
return path; // Don't forget mxFree !!!
}
希望您会喜欢这个替代解决方案。它的主要优点是您不必费心确保在 matlab 中创建的结构适合 c 端代码。一切都委托给 .mex 文件(即在 c 中)并作为句柄传递给 matlab。