获取libstruct在matlab中为dll指针参数工作 [英] getting libstruct to work in matlab for dll pointer argument

查看:594
本文介绍了获取libstruct在matlab中为dll指针参数工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  typedef struct _sixenseControllerData {
float pos [3];
float rot_mat [3] [3];
float joystick_x;
float joystick_y;
浮点触发器
...
} sixenseControllerData;

和我可以调用的功能:

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

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



所以我尝试这样的libstruct:

  libstruct('sixenseControllerData')

我收到错误:

  ???错误使用==> feval 
未定义的函数或变量'lib.sixenseControllerData'。

==>中的错误libstruct at 15
ptr = feval(['lib。'structtype]);

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



完整的头文件在这里可用:
https://github.com/rll/sixense/blob /master/include/sixense.h

解决方案

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

  s = libstruct('s_sixenseControllerData'); 

我们可以通过指示MATLAB生成一个原型文件

 >> 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' '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 限制 $ c>它不支持嵌套结构,特别是如果一个结构包含指向另一个结构的指针(在这种情况下是一个数组):


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


所以你将无法直接在MATLAB端创建 sixenseAllControllerData 结构,它在C头文件中定义为:

  typedef struct _sixenseAllControllerData {
sixenseControllerData controllers [4];
} sixenseAllControllerData;

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



在您的情况下,更改生成的 sixense_proto.m file into:

  structs.s_sixenseAllControllerData.members = struct(... 
'controllers1' 's_sixenseControllerData',...
'controllers2','s_sixenseControllerData',...
'controllers3','s_sixenseControllerData',...
'controllers4','s_sixenseControllerData');

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

  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)

完全不同的解决方案是写一个 MEX-function 与库连接。它就像任何其他C / C ++代码,只使用 mxArray 和MX-API与MATLAB接口...






示例:



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



helper.h



  #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



mylib.h



  #ifndef MYLIB_H 
#define MYLIB_H
#includehelper.h

typedef struct _mystruct {
int pos [3];
双重值;
} mystruct;

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

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

#endif



mylib.c



  #define EXPORT_FCNS 
#includehelper.h
#includemylib.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; (i = 0; i <2; i ++){
myfunc(&(s-> arr [i]))

}
s-> num = 99;
}

将上述编译成DLL后,我们生成初始原型文件: / p>

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

我如前所述编辑原型文件:

 函数[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

现在我们终于可以调用DLL暴露的函数:

 %//使用proto文件加载库
loadlibrary('./ mylib.dll',@mylib_proto)

%//使用指向struct
s = struct('pos',[0,0,0],'value',0)的指针调用第一个函数;
ss = libstruct('s_mystruct',s);
calllib('mylib','myfunc',ss)
get(ss)

%//调用第二个函数,指向struct包含struct
的数组xx = libstruct('s_mystruct2');
xx.arr1 = libstruct('s_mystruct');
xx.arr2 = libstruct('s_mystruct');
calllib('mylib','myfunc2',xx)
get(xx)

%//清除引用和卸载库
清除ss xx
unloadlibrary mylib


I'm trying to call a dll function in matlab. I have a C++ struct as shown in sixense.h:

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

and functions I could call:

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

I can easily get this to work with calllib('sixense','sixenseInit') since there is no input, but for the function sixenseGetAllNewestData I need to have a struct pointer. I realize that libstruct is what I need to use. However, I don't seem to be doing it right.

So I tried libstruct like so:

libstruct('sixenseControllerData')

and I get the error:

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

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

EDIT: here is my current unedited proto file: http://pastebin.com/PemmmMqF

the full header file is available here: https://github.com/rll/sixense/blob/master/include/sixense.h

解决方案

For C structures, loadlibrary generates types named: s_{NAME} where {NAME} is the name of the structure. In your case we create a pointer as:

s = libstruct('s_sixenseControllerData');

We can see this fact by instructing MATLAB to generate a prototype file:

>> loadlibrary('sixense', 'sixense.h', 'proto','sixense_proto')

A prototype file is a file of MATLAB commands which we can modify and use in place of a header file. In this case, the file will contain something like:

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');
....

Unfortunately, a limitation of loadlibrary is that it does not support nested structure very well, especially if a structure contains a pointer to another structure (or an array in this case):

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.

So you will not be able to directly create the sixenseAllControllerData structure on the MATLAB side, which is defined in the C header file as:

typedef struct _sixenseAllControllerData {
    sixenseControllerData controllers[4];
} sixenseAllControllerData;

According to the following discussion, one workaround is to "unroll"/"flatten" the array into separate variables. You can either do this in a copy of the header file, or making the changes in the generated prototype file (which I think is the preferred way). You can do this without having to recompile the shared library.

In your case, change the nested structure in the generated sixense_proto.m file into:

structs.s_sixenseAllControllerData.members = struct(...
    'controllers1', 's_sixenseControllerData', ...
    'controllers2', 's_sixenseControllerData', ...
    'controllers3', 's_sixenseControllerData', ...
    'controllers4', 's_sixenseControllerData');

Now we can create a pointer to this structure, and call the C method:

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)

A completely different solution is to write a MEX-function to interface with the library. It is just like any other C/C++ code, only using mxArray and the MX-API to interface with MATLAB...


Example:

To test the above, I created a simple DLL with similar structures, and implemented the above solution. Here is the code if someone wants to test it:

helper.h

#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

mylib.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.c

#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;
}

After compiling the above into a DLL, we generate the initial prototype file:

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

I edit the prototype file as described before:

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

Now we can finally invoke functions exposed by the DLL:

%// 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

这篇关于获取libstruct在matlab中为dll指针参数工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆