【问题标题】:Calling methods of C++ class in MEX from MATLAB从 MATLAB 调用 MEX 中 C++ 类的方法
【发布时间】:2013-02-14 04:27:30
【问题描述】:

我有一个 DLP 套件,我需要使用 C++ API 通过 MATLAB 进行控制。

说,我在一个名为 dlp_controller.cpp/.c 的 mex 文件中有使用 C/C++ 处理 {load_data, load_settings,display_data} 的函数/方法。

我知道我可以使用 MATLAB 调用 dlp_controller();

有没有办法直接从 MATLAB 调用这个 mex 的方法?

假设我的 dlp_controller.cpp mex 看起来像:

class dlp{ ... }
dlp::dlp{ ... }
dlp::load_data{ ... }
dlp::load_settings{ ... }
dlp::display_data{ ... }

void mexFunction(int nlhs, mxArray *[],int nrhs, const mxArray *prhs[]{ ... }

我可以从 MATLAB 中以某种方式调用像 dlp_controller.load_data 这样的方法吗?注意:解决方法是将变量发送到dlp_controller,并使用该变量和传递的数据在内部调用函数。

【问题讨论】:

    标签: c++ c matlab methods mex


    【解决方案1】:

    AFAIK,没有直接的方法可以做到这一点,因为mexFunction 接口相当扁平。但是,我可以想到一些不同的解决方法,这应该会让你接近。根据您的需要选择最好的。

    1. 最简单的方法是在 mex 函数中创建 dlp 类的全局实例。使 mex 函数的第一个参数调用一个字符串,该字符串指示要调用全局对象的哪个成员函数。明显的缺点是您将 mex 函数变成了单例。

    2. 如果您需要多个 dlp 实例,您可以在 mex 函数中创建一些全局容器,例如 std::map<std::string, dlp>,然后在 MATLAB 中通过某个名称引用每个 dlp 实例。例如,要创建一个新实例,您需要使用 map 中尚不存在的名称调用 mex 函数。然后,您可以使用此名称调用 mex 函数,该字符串指示要调用的成员函数,以及要传递给成员函数的任何参数。还可以设置一些约定,通过它您可以从map 中删除dlp 实例。

    3. 与第二个解决方案类似,您可以将 句柄 返回给每个实例,而不是命名 dlp 实例。例如,创建一个全局 std::set<dlp *> 并且当您使用 mex 函数创建一个新的 dlp 实例时,将其添加到 set 并将指向已分配对象的指针的副本返回给 MATLAB(将其粘贴在标量中mxUINT64_CLASS 类型的变量)。随后调用该对象上的成员函数会将此 handle 变量从 MATLAB 传递给 mex 函数,您将在 mex 文件中适当地转换它,在 set 中找到它并调用成员函数.

    这些方法都不是特别漂亮,但这是我所知道的从 mex 文件中调用 C++ 类的成员函数的唯一方法。

    【讨论】:

    • 我会尝试第一个,因为不需要多个 DLP 支持。谢谢。
    • 如果您需要动态分配您的单例,请务必使用mexAtExit() 注册清理函数。还要考虑mexLock()mexUnlock() 以确保您的实例不会在您不期望的时候消失。这一切都是因为 MEX DLL(或 .so)在必要时动态加载,并在 MATLAB 认为不再需要时卸载。
    • 这些方法都有效。但是,只有最后两个允许类的多个实例,如果在 MATLAB 中清除它们的句柄,则建议的方法都不会破坏这些实例,而无需先手动调用析构函数。由于这个原因,您可能会泄漏内存(直到 mex dll 被卸载)。如果您的实例消耗大量内存并且您按顺序创建大量它们,这将给您带来麻烦。使用 Sam Roberts 建议的包装器解决了这个问题 - 当 MATLAB 中不再存在对它们的引用时,实例会自动销毁。
    【解决方案2】:

    您可能想看看这个提交给 MATLAB Central 的内容。据我所知,它展示了最佳实践,是根据包括 MathWorkers 在内的许多人的新闻组建议开发的。

    http://www.mathworks.co.uk/matlabcentral/fileexchange/38964-example-matlab-class-wrapper-for-a-c++-class

    【讨论】:

      【解决方案3】:

      最简单的方法是设计一个保留类对象实例的 MEX 包装器,然后调度对该 MEX 二进制文件的调用。我为那些试图用 C++ 开发 MEX 包装器的人制作了一个库。

      https://github.com/kyamagu/mexplus

      这是一个快速的 sn-p。

      // C++ class to be wrapped.
      class Database;
      // Instance session storage.
      template class mexplus::Session<Database>;
      // Constructor.
      MEX_DEFINE(new) (int nlhs, mxArray* plhs[],
                       int nrhs, const mxArray* prhs[]) {
        InputArguments input(nrhs, prhs, 1);
        OutputArguments output(nlhs, plhs, 1);
        output.set(0, Session<Database>::create(
            new Database(input.get<std::string>(0))));
      }
      // Destructor.
      MEX_DEFINE(delete) (int nlhs, mxArray* plhs[],
                          int nrhs, const mxArray* prhs[]) {
        InputArguments input(nrhs, prhs, 1);
        OutputArguments output(nlhs, plhs, 0);
        Session<Database>::destroy(input.get(0));
      }
      // Member method.
      MEX_DEFINE(query) (int nlhs, mxArray* plhs[],
                         int nrhs, const mxArray* prhs[]) {
        InputArguments input(nrhs, prhs, 2);
        OutputArguments output(nlhs, plhs, 1);
        const Database& database = Session<Database>::getConst(input.get(0));
        output.set(0, database.query(input.get<string>(1)));
      }
      // And so on...
      MEX_DISPATCH
      

      【讨论】:

        【解决方案4】:

        对于它的价值,这是我对这个问题的看法。请参阅this GitHub repo 中的示例 MEX 文件和类包装器。这是我不久前想出的一个解决方案,我刚刚在制作a related question 的答案时发现了这个问题。希望这会对某人有所帮助。

        设计目标

        • 管理 C++ 类的多个持久实例
        • MATLAB 中使用的小连续整数句柄(不是强制转换指针)
        • 透明地处理资源管理(即 MATLAB 从不负责为 C++ 类分配的内存):
          1. 如果 MATLAB 未能发出“删除”操作,则不会发生内存泄漏。
          2. MEX 文件过早卸载时自动解除分配。
        • 防止模块过早卸载
        • 在不检查幻数的情况下隐式验证句柄的有效性
        • 没有模仿 mexFunction 的包装类或函数,只是一个直观的 mexFunction 中的 switch-case 块。

        请注意,应该在不考虑任何 MATLAB 类的情况下实现这些目标,但这也有助于解决内存管理问题。因此,生成的 MEX 文件可以安全地直接使用(但不太优雅)。

        实施概述

        对于您的 C++ 类class_typemexFunction 使用静态数据存储来保存整数句柄和指向动态分配的类实例的智能指针的持久(在对 mexFunction 的调用之间)表。 std::map 用于此目的,它有助于定位已知句柄,只保证您的类的有效实例存在:

        typedef unsigned int handle_type;
        std::map<handle_type, std::shared_ptr<class_type>>
        

        当 (1) 通过“删除”操作删除表元素或 (2) 卸载 MEX 文件时,std::shared_ptr 负责解除分配。

        为防止在 MATLAB 类实例存在时卸载 MEX 文件,每次创建新的 C++ 类实例时都会调用 mexLock,从而增加 MEX 文件的锁定计数。每次删除 C++ 实例时,都会调用 mexUnlock,从锁计数中删除一个锁。

        使用

        1. [In .cpp] 在 Actions 枚举中枚举不同的操作(例如,新建、删除、插入等)。对于每个枚举操作,指定一个字符串(例如“new”、“delete”、“insert”等)作为第一个参数传递给 MATLAB 中的 MEX 函数。
        2. [在 .cpp 中] 自定义 mexFunction 正文中 switch 语句中每个操作的处理(例如,调用相关的 C++ 类方法)。
        3. [In .m](可选)创建一个派生自 cppclass 的类,为您的类所需的操作创建简单的方法。

        要求

        具有以下 C++11 特性的现代编译器:

        • shared_ptr
        • auto
        • enum class
        • initializer_list(用于const map初始化)

        Visual Studio 2013、最近的 GCC(可能与 -std=c++11),以及自 3.1 以来的 Clang。

        Source

        【讨论】:

          【解决方案5】:

          替代设计,更简洁的用例,是定义一个带有init,fcnA,fcnB,fcnC,......方法......等的单例类,以及相应的轻量级包装器...... 然后从 MEX 调用包装器。

          例如

          class A {
            public:
               A getInstance() {
                  if ( !instance )
                       instance = new A(...);
                  return instance;
               }
          
              void fcnA(T1 a1, T2 a2) {
                     //yada yada
               }
          
            private:
              static A* instance;
          };
          A::instance = NULL;
          
          //begin wrappers for marshalling to class method
          A* getInstance( ) {
             return A::getInstance();
          }
          
          void fcnA(T1 a1, T2 a2) {
             getInstance()->fcnA(a1,a2);
          }
          
          //ad nauseum
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多