Calling methods of C++ class in MEX from MATLAB

后端 未结 5 2086
一向
一向 2021-01-03 09:59

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}

相关标签:
5条回答
  • 2021-01-03 10:30

    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.

    0 讨论(0)
  • 2021-01-03 10:32

    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

    0 讨论(0)
  • 2021-01-03 10:52

    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
    
    0 讨论(0)
  • 2021-01-03 10:54

    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):
      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 (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.
    2. [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).
    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

    • 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
    0 讨论(0)
  • 2021-01-03 10:54

    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
    
    0 讨论(0)
提交回复
热议问题