3

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

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

我知道我可以dlp_controller();用 MATLAB 打电话。

有没有办法可以直接从 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[]{ ... }

我可以以某种方式调用dlp_controller.load_dataMATLAB 之类的方法吗?注意:一种解决方法是发送一个变量dlp_controller并使用该变量和传递的数据在内部调用该函数。

4

5 回答 5

7

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

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

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

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

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

于 2013-02-28T05:19:05.690 回答
4

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

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

于 2013-02-28T10:35:49.757 回答
2

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

https://github.com/kyamagu/mexplus

这是一个快速的片段。

// 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
于 2014-03-17T19:14:44.620 回答
1

对于它的价值,这是我对这个问题的看法。请参阅此 GitHub存储库中的示例 MEX 文件和类包装器。这是我不久前提出的一个解决方案,我刚刚在为相关问题制定答案时发现了这个问题。希望这对某人有所帮助。

设计目标

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

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

实施概述

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

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

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

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

利用

  1. [在 .cpp 中] 在 Actions 枚举中枚举不同的操作(例如,新建、删除、插入等)。对于每个枚举动作,指定一个字符串(例如“new”、“delete”、“insert”等)作为第一个参数传递给 MATLAB 中的 MEX 函数。
  2. [在.cpp中] 自定义对switch语句体中每个动作的处理mexFunction(例如调用相关的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。

资源

于 2015-09-11T20:52:03.483 回答
0

替代设计,对于这里的用例来说更简洁,是定义一个带有 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
于 2013-02-28T06:35:46.933 回答