让libstruct在matlab中为dll指针参数工作

时间:2013-08-06 00:13:55

标签: matlab dll loadlibrary

我正在尝试在matlab中调用dll函数。我有一个C ++结构,如sixense.h所示:

typedef struct _sixenseControllerData {
  float pos[3];
  float rot_mat[3][3];
  float joystick_x;
  float joystick_y;
  float trigger;
  ...
} sixenseControllerData;

我可以打电话的功能:

SIXENSE_EXPORT int sixenseInit( void );
SIXENSE_EXPORT int sixenseGetAllNewestData( sixenseAllControllerData * );

我可以很容易地使用calllib('sixense','sixenseInit')来处理它,因为没有输入,但是对于函数sixenseGetAllNewestData,我需要有一个结构指针。我意识到libstruct是我需要使用的东西。但是,我似乎并没有做得对。

所以我尝试了类似libstruct:

libstruct('sixenseControllerData')

我收到错误:

??? Error using ==> feval
Undefined function or variable 'lib.sixenseControllerData'.

Error in ==> libstruct at 15
    ptr=feval(['lib.' structtype]);

编辑:这是我目前未经编辑的原型文件: http://pastebin.com/PemmmMqF

这里提供了完整的头文件: https://github.com/rll/sixense/blob/master/include/sixense.h

1 个答案:

答案 0 :(得分:4)

对于C结构,loadlibrary生成名为s_{NAME}的类型,其中{NAME}是结构的名称。在您的情况下,我们创建pointer作为:

s = libstruct('s_sixenseControllerData');

我们可以通过指示MATLAB生成prototype file

来看到这一事实
>> loadlibrary('sixense', 'sixense.h', 'proto','sixense_proto')

原型文件是MATLAB命令的文件,我们可以修改它们来代替头文件。在这种情况下,该文件将包含以下内容:

sixense_proto.m

...
structs.s_sixenseControllerData.members = struct('pos', 'single#3', 'rot_mat', 'single#9', 'joystick_x', 'single', 'joystick_y', 'single', 'trigger', 'single', 'buttons', 'uint32', 'sequence_number', 'uint8', 'rot_quat', 'single#4', 'firmware_revision', 'uint16', 'hardware_revision', 'uint16', 'packet_type', 'uint16', 'magnetic_frequency', 'uint16', 'enabled', 'int32', 'controller_index', 'int32', 'is_docked', 'uint8', 'which_hand', 'uint8', 'hemi_tracking_enabled', 'uint8');
structs.s_sixenseAllControllerData.members = struct('controllers', 's_sixenseControllerData#4');
....

不幸的是,loadlibrary sixenseAllControllerData是它不能很好地支持嵌套结构,特别是如果一个结构包含指向另一个结构的指针(或者在这种情况下是一个数组):

  

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

因此,您将无法在MATLAB端直接创建typedef struct _sixenseAllControllerData { sixenseControllerData controllers[4]; } sixenseAllControllerData; 结构,该结构在C头文件中定义为:

sixense_proto.m

根据以下limitation,一种解决方法是将数组“展开”/“展平”为单独的变量。您可以在头文件的副本中执行此操作,也可以在生成的原型文件中进行更改(我认为这是首选方法)。您无需重新编译共享库即可完成此操作。

在您的情况下,将生成的structs.s_sixenseAllControllerData.members = struct(... 'controllers1', 's_sixenseControllerData', ... 'controllers2', 's_sixenseControllerData', ... 'controllers3', 's_sixenseControllerData', ... 'controllers4', 's_sixenseControllerData'); 文件中的嵌套结构更改为:

s = libstruct('s_sixenseAllControllerData');
s.controllers1 = libstruct('s_sixenseControllerData');
s.controllers2 = libstruct('s_sixenseControllerData');
s.controllers3 = libstruct('s_sixenseControllerData');
s.controllers4 = libstruct('s_sixenseControllerData');

out = calllib('sixense', 'sixenseGetAllNewestData', s);
get(s)

discussion我们可以创建一个指向这个结构的指针,并调用C方法:

mxArray

完全不同的解决方案是将Now写入与库的接口。它就像任何其他C / C ++代码一样,只使用#ifndef HELPER_H #define HELPER_H #ifdef _WIN32 #ifdef EXPORT_FCNS #define EXPORTED_FUNCTION __declspec(dllexport) #else #define EXPORTED_FUNCTION __declspec(dllimport) #endif #else #define EXPORTED_FUNCTION #endif #endif 和MX-API与MATLAB接口......


实施例

为了测试上面的内容,我创建了一个结构相似的简单DLL,并实现了上述解决方案。如果有人想测试它,这是代码:

helper.h

#ifndef MYLIB_H
#define MYLIB_H
#include "helper.h"

typedef struct _mystruct {
    int pos[3];
    double value;
} mystruct;

typedef struct _mystruct2 {
    mystruct arr[2];
    int num;
} mystruct2;

EXPORTED_FUNCTION void myfunc(mystruct *);
EXPORTED_FUNCTION void myfunc2(mystruct2 *);

#endif

mylib.h

#define EXPORT_FCNS
#include "helper.h"
#include "mylib.h"

void myfunc(mystruct *s)
{
    s->pos[0] = 10;
    s->pos[1] = 20;
    s->pos[2] = 30;
    s->value = 4.0;
}

void myfunc2(mystruct2 *s)
{
    int i;
    for(i=0; i<2; i++) {
        myfunc(&(s->arr[i]));
    }
    s->num = 99;
}

mylib.c

loadlibrary('./mylib.dll', './mylib.h', 'mfilename','mylib_proto')
unloadlibrary mylib

将上述内容编译成DLL后,我们生成初始原型文件:

function [methodinfo,structs,enuminfo,ThunkLibName] = mylib_proto()
    MfilePath = fileparts(mfilename('fullpath'));
    ThunkLibName = fullfile(MfilePath,'mylib_thunk_pcwin64');

    enuminfo = [];

    structs = [];
    structs.s_mystruct.members = struct('pos','int32#3', 'value','double');
    structs.s_mystruct2.members = struct('arr1','s_mystruct', ...
        'arr2','s_mystruct', 'num','int32');

    ival = {cell(1,0)};
    methodinfo = struct('name',ival, 'calltype',ival, 'LHS',ival, ...
        'RHS',ival, 'alias',ival, 'thunkname',ival);

    methodinfo.thunkname{1} = 'voidvoidPtrThunk';
    methodinfo.name{1} = 'myfunc';
    methodinfo.calltype{1} = 'Thunk';
    methodinfo.LHS{1} = [];
    methodinfo.RHS{1} = {'s_mystructPtr'};

    methodinfo.thunkname{2} = 'voidvoidPtrThunk';
    methodinfo.name{2} = 'myfunc2';
    methodinfo.calltype{2} = 'Thunk';
    methodinfo.LHS{2} = [];
    methodinfo.RHS{2} = {'s_mystruct2Ptr'};
end

我按照前面的描述编辑原型文件:

%// load library using proto file
loadlibrary('./mylib.dll', @mylib_proto)

%// call first function with pointer to struct
s = struct('pos',[0,0,0], 'value',0);
ss = libstruct('s_mystruct',s);
calllib('mylib', 'myfunc', ss)
get(ss)

%// call second function with pointer to struct containing array of struct
xx = libstruct('s_mystruct2');
xx.arr1 = libstruct('s_mystruct');
xx.arr2 = libstruct('s_mystruct');
calllib('mylib', 'myfunc2', xx)
get(xx)

%// clear references and unload library
clear ss xx
unloadlibrary mylib

现在我们终于可以调用DLL公开的函数了:

{{1}}