从MATLAB中调用MEX中的C ++类方法
我有一个DLP工具包,我需要使用C ++ API通过MATLAB进行控制。
比如说,我在一个名为dlp_controller.cpp / .c的mex文件中使用C / C ++来实现{load_data, load_settings,display_data}
函数/方法。
我知道我可以调用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[]{ ... }
我可以以某种方式从MATLAB调用像dlp_controller.load_data
这样的方法吗? 注意:解决方法可以是将变量发送到dlp_controller
并使用该函数和传递的数据在内部调用该函数。
AFAIK,没有直接的方法,因为mexFunction
接口相当平坦 。 但是,我可以想到一些不同的解决方法,可以让你接近。 根据您的需求选择最佳的一个。
-
最简单的方法是在mex函数中创建
dlp
类的全局实例。 使mex函数的第一个参数调用一个字符串,该字符串指示调用全局对象的哪个成员函数。 明显的缺点是你已经将你的mexfunction变成了单身。 -
如果您需要多个
dlp
实例,可以在mex函数中创建一些全局容器,例如std::map
,然后在MATLAB中通过某个名称引用每个dlp
实例。 例如,要创建一个新实例,您可以使用map
尚不存在的名称调用mex函数。 然后,您可以使用此名称调用mex函数,该字符串指示要调用的成员函数以及要传递给成员函数的任何参数。 还设置了一些约定,您可以通过该约定从map
删除dlp
实例。 -
与第二个解决方案类似,您可以将句柄返回给每个实例,而不是命名
dlp
实例。 例如,创建一个全局std::set
,当你有mex函数创建一个新的dlp
实例时,将它添加到set
并将指向分配对象的指针的副本返回给MATLAB(将其粘贴到一个mxUINT64_CLASS
类型的标量变量。 对该对象调用成员函数的后续调用将把这个句柄变量传递给MATLAB中的mex函数,你将在mex文件中适当地转换它,在set
找到它并调用成员函数。
这些方法都不是特别漂亮,但这些是我所知道的从mex文件中调用C ++类的成员函数的唯一方法。
您可能想看一下这个提交到MATLAB Central的内容。 据我所知,它展示了最佳实践,由包括MathWorkers在内的许多人提供新闻组建议。
最简单的方法是设计一个MEX包装器,它保存类对象的实例,并调度对该MEX二进制文件的调用。 我为那些试图用C ++开发MEX包装器的人创建了一个库。
https://github.com/kyamagu/mexplus
这是一个快速片段。
// C++ class to be wrapped. class Database; // Instance session storage. template class mexplus::Session; // 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 ::create( new Database(input.get(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::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 ::getConst(input.get(0)); output.set(0, database.query(input.get(1))); } // And so on... MEX_DISPATCH
对于它的价值,这是我对这个问题的看法。 请参阅此GitHub存储库中的示例MEX文件和类包装器。 这是我不久前提出的一个解决方案,我刚刚在制作相关问题的答案时发现了这个问题 。 希望这对某人有所帮助。
设计目标
- 管理C ++类的多个持久化实例
- MATLAB中使用的小连续整数句柄(不是指针)
- 透明地处理资源管理(即MATLAB从不负责为C ++类分配的内存):
- 如果MATLAB无法发出“删除”操作,则不会泄漏内存。
- 如果MEX文件过早卸载,则自动释放。
- 防止过早的模块卸载
- 在不检查幻数的情况下隐式validation句柄的有效性
- 没有包装类或模仿mexFunction的函数,只是mexFunction中的直观switch-case块。
请注意,这些目标应该不考虑任何MATLAB类,但也可以帮助解决内存管理问题。 因此,生成的MEX文件可以安全地直接使用(但不是太优雅)。
实施概述
对于C ++类class_type
, mexFunction
使用静态数据存储来保存整数句柄的持久性(在调用mexFunction
之间)和动态分配的类实例的智能指针。 std::map
用于此目的,这有助于定位已知句柄,只保证您的类的有效实例存在:
typedef unsigned int handle_type; std::map>
当(1)通过“删除”操作擦除表元素或(2)卸载MEX文件时, std::shared_ptr
负责解除分配。
为了防止在MATLAB类实例存在时卸载MEX文件,每次创建新的C ++类实例时都会调用mexLock
,这会增加MEX文件的锁定计数。 每次删除C ++实例时mexUnlock
调用mexUnlock
,从锁定计数中删除一个锁。
使用
- [In .cpp]在Actions枚举中枚举不同的动作(例如New,Delete,Insert等)。 对于每个枚举操作,指定一个字符串(例如“new”,“delete”,“insert”等)作为MATLAB中MEX函数的第一个参数传递。
- [In .cpp]自定义
mexFunction
主体中switch语句中每个动作的处理(例如,调用相关的C ++类方法)。 - [In .m](可选)创建一个派生自cppclass的类,为类所需的操作创建简单方法。
要求
具有以下C ++ 11function的现代编译器:
-
shared_ptr
-
auto
-
enum class
-
initializer_list
(用于const map
初始化)
Visual Studio 2013,最近的GCC(可能是-std=c++11
)和Clang从3.1开始。
资源
- C ++组件(class_wrapper_template.cpp) (
mexFunction
和实例映射)。 为要管理的C ++重命名和编辑它。 - 基类(cppclass.m),用于通过MEX文件管理单个实例
替代设计,这里的用例更清晰,是使用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