【问题标题】:getting libstruct to work in matlab for dll pointer argument让 libstruct 在 matlab 中为 dll 指针参数工作
【发布时间】:2013-08-06 00:13:55
【问题描述】:

我正在尝试在 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

【问题讨论】:

    标签: matlab dll loadlibrary


    【解决方案1】:

    对于 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 中的 limitation 不能很好地支持嵌套结构,尤其是如果一个结构包含指向另一个结构(或本例中的数组)的指针:

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

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

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

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

    在您的情况下,将生成的sixense_proto.m 文件中的嵌套结构更改为:

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

    Now我们可以创建一个指向这个结构的指针,然后调用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
    #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;
    }
    

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

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

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

    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
    

    现在我们终于可以调用 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
    

    【讨论】:

    • 哇,感谢 Amro!很详细的回答!我会经历这一切并回复你。
    • 我的问题中没有嵌套结构,但我认为我的问题是我有一个 3x3 矩阵。原型似乎认为类型是“single#”,其他一切要么只是一个单一的,要么就像“single#3”。我该如何解决这个问题?
    • @mugetsu:你确实有嵌套结构,函数sixenseGetAllNewestData 需要一个指向sixenseAllControllerData 的指针,其中包含sixenseControllerData 的数组。至于 3x3 矩阵 rot_mat,在我的例子中,MATLAB 正确地生成了一个带有 single#9 类型的 proto 文件(如您在上面的回答中所见)。您使用的是什么平台、MATLAB 版本以及您使用的是什么编译器?
    • 我在 windows 7 matlab 2007a 上。我正在使用 Visual Studio C++ Express 2010。
    • 谢谢!看了几遍,终于搞定了!
    猜你喜欢
    • 2011-11-18
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-29
    • 2011-02-04
    相关资源
    最近更新 更多