简体   繁体   中英

Calling methods of C++ class in MEX from MATLAB

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.

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.

  1. 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.

  2. 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 each dlp 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 the map . 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 a dlp instance from the map .

  3. Similar to the second solution, instead of naming the dlp instances you could return handles to each instance. For instance, create a global std::set<dlp *> and when you have the mex function create a new dlp instance, add it to the set and return a copy of the pointer to the allocated object to MATLAB (stick it in a scalar variable of type mxUINT64_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 the set 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.

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

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

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 (ie MATLAB never responsible for memory allocated for C++ classes):
    1. No memory leaked if MATLAB fails to issue "delete" action.
    2. 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

  1. [In .cpp] Enumerate the different actions (eg New, Delete, Insert, etc.) in the Actions enum. For each enumerated action, specify a string (eg "new", "delete", "insert", etc.) to be passed as the first argument to the MEX function in MATLAB.
  2. [In .cpp] Customize the handling for each action in the switch statement in the body of mexFunction (eg call the relevant C++ class method).
  3. [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 (for const map initialization)

Visual Studio 2013, recent GCC (possibly with -std=c++11 ), and Clang since 3.1.

Source

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.

eg

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM