问题
I have a DLP kit which I need to control via MATLAB using a C++ API.
Say, I have functions/methods using C/C++ for {load_data, load_settings,display_data}
in a mex file named dlp_controller.cpp/.c.
I know I can call dlp_controller();
with MATLAB.
Is there a way I can call the method of this mex from MATLAB directly?
Say my dlp_controller.cpp mex looks as:
class dlp{ ... }
dlp::dlp{ ... }
dlp::load_data{ ... }
dlp::load_settings{ ... }
dlp::display_data{ ... }
void mexFunction(int nlhs, mxArray *[],int nrhs, const mxArray *prhs[]{ ... }
Can I somehow call the methods like dlp_controller.load_data
from MATLAB? NOTE: A workaround can be to send a variable to dlp_controller
and call the function internally using that and the data passed.
回答1:
AFAIK, there's no straightforward way to do this, because the mexFunction
interface is rather flat. However, there are a few different workarounds I can think of that should get you close. Choose the best one depending on your needs.
The easiest is to create a global instance of the
dlp
class in your mex function. Make the first parameter of the mex function call a string that instructs which member function of the global object is to be invoked. The obvious drawback is that you've turned your mex function into a singleton.If you need more than one
dlp
instance, you could create some global container in the mex function,std::map<std::string, dlp>
for instance, and then refer to eachdlp
instance by some name from within MATLAB. For instance, to create a new instance you'd call the mex function with a name that doesn't already exist in themap
. Then you could call the mex function with this name, a string indicating which member function to invoke, and any parameters to be passed to the member function. Also set up some convention by which you can erase adlp
instance from themap
.Similar to the second solution, instead of naming the
dlp
instances you could return handles to each instance. For instance, create a globalstd::set<dlp *>
and when you have the mex function create a newdlp
instance, add it to theset
and return a copy of the pointer to the allocated object to MATLAB (stick it in a scalar variable of typemxUINT64_CLASS
). Subsequent calls to invoke member functions on that object will pass this handle variable to the mex function from MATLAB, you'll cast it appropriately within the mex file, find it within theset
and invoke member functions.
None of these methods are particularly pretty, but these are the only ways I know of to invoke member functions of a C++ class from within a mex file.
回答2:
You might like to take a look at this submission to MATLAB Central. As far as I know it demonstrates best practice, developed with newsgroup advice from many including MathWorkers.
http://www.mathworks.co.uk/matlabcentral/fileexchange/38964-example-matlab-class-wrapper-for-a-c++-class
回答3:
The easiest is to design a MEX wrapper that keeps an instance of the class object, and dispatch a call to that MEX binary. I have made a library for those trying to develop a MEX wrapper in C++.
https://github.com/kyamagu/mexplus
Here is a quick snippet.
// 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:
For what it's worth, here's my take on the problem. Please see the example MEX file and class wrapper in this GitHub repo. This is a solution I came up with a little while ago, and I just now found this question while crafting an answer to a related question. Hopefully this will be helpful to someone.
Design goals
- Manage multiple persistent instances of a C++ class
- Small consecutive integer handles used in MATLAB (not cast pointers)
- Transparently handle resource management (i.e. MATLAB never responsible for memory allocated for C++ classes):
- No memory leaked if MATLAB fails to issue "delete" action.
- Automatic deallocation if MEX-file prematurely unloaded.
- Guard against premature module unloading
- Validity of handles implicitly verified without checking a magic number
- No wrapper class or functions mimicking mexFunction, just an intuitive switch-case block in mexFunction.
Note that these goals should be acheved without regard to any MATLAB class, but which can also help address memory management issues. As such, the resulting MEX-file can safely be used directly (but not too elegantly).
Implementation Overview
For your C++ class, class_type
, the mexFunction
uses static data storage to hold a persistent (between calls to mexFunction
) table of integer handles and smart pointers to dynamically allocated class instances. A std::map
is used for this purpose, which facilitates locating known handles, for which only valid instances of your class are guaranteed to exist:
typedef unsigned int handle_type;
std::map<handle_type, std::shared_ptr<class_type>>
A std::shared_ptr
takes care of deallocation when either (1) a table element is erased via the "delete" action or (2) the MEX-file is unloaded.
To prevent the MEX-file from unloading while a MATLAB class instances exist, mexLock
is called each time a new C++ class instance is created, adding to the MEX-file's lock count. Each time a C++ instance is deleted mexUnlock
is called, removing one lock from the lock count.
Use
- [In .cpp] Enumerate the different actions (e.g. New, Delete, Insert, etc.) in the Actions enum. For each enumerated action, specify a string (e.g. "new", "delete", "insert", etc.) to be passed as the first argument to the MEX function in MATLAB.
- [In .cpp] Customize the handling for each action in the switch statement in the body of
mexFunction
(e.g. call the relevant C++ class method). - [In .m] (Optional) Create a class that derives from cppclass, creating simple methods for the actions required by your class.
Requirements
A modern compiler with the following C++11 features:
shared_ptr
auto
enum class
initializer_list
(forconst map
initialization)
Visual Studio 2013, recent GCC (possibly with -std=c++11
), and Clang since 3.1.
Source
- C++ component (class_wrapper_template.cpp) (
mexFunction
and instance map). Rename and edit this for the C++ you want to manage. - Base class (cppclass.m) for managing a single instance via the MEX file
回答5:
Alternative design, cleaner for the use case here, is to define a singleton class with init, fcnA, fcnB, fcnC, ... methods ... etc, with corresponding lightweight wrappers.. and then invoke the wrappers from the MEX.
e.g.
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
来源:https://stackoverflow.com/questions/15127773/calling-methods-of-c-class-in-mex-from-matlab