Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compile Matlab class into C lib?

The origin of this question is from here How to use "global static" variable in matlab function called in c.

I'm trying to encapsulate the "global variable" into an object. However I don't know how to export the matlab class to c++ using MATLAB Compiler (mcc)

To do this I just tried the standard command

Matlab Command

mcc -W cpplib:Vowel4 -T link:lib Vowel4.m

Matlab Script

classdef Vowel4

  properties
    x
    y
  end

  methods
    Vowel4
    A
    B
  end

end

The generated lib is actually stand-alone functions rather than c++ class.

How can I compile classes in Matlab into c++ classes?

I've been searching for an answer but didn't find one.

Obviously the matlab command is not suitable for this scenario. However I cannot find any information on building Matlab classes into c++ classes.

========================== Edit ========================

The actual cpp code is as follows: @Alan

mclInitializeApplication(NULL, 0);
loadDataInitialize();
soundByCoefInitialize();
loadData(); 

mwArray F(4, 1, mxDOUBLE_CLASS);
float test[4];

for ( ;; ){
    const Frame frame = controller.frame();
    const FingerList fingers = frame.fingers();
    if ( !fingers.empty() ){
        for ( int i = 0; i < 4; i ++ ){
            double v = fingers.count() > i ? (fingers[i].tipPosition().y / 50) - 2 : 0;
            F(i+1,1) = v;
            test[i] = v;
            cout << v << ' ';
        }
        cout << endl;
        soundByCoef(F);
    }
}

Here the matlabA() corresponds to the loadData(), which loads the data, and soundByCoef(F) corresponds to the matlabB(), which do the job in the main loop.

like image 794
SolessChong Avatar asked Feb 17 '23 23:02

SolessChong


2 Answers

As noted by Alan, I was only suggesting using handle class as a container for your global variables (with the benefit that such an object would be passed by reference). The created object is not intended to be directly manipulated by your C++ code (it will be stored in the generic mxArray/mwArray C/C++ struct).

As far as I know, you cannot directly compile classdef-style MATLAB classes into proper C++ classes when building shared libraries using the MATLAB Compiler. It only supports building regular functions. You could create functional interfaces to MATLAB class member methods, but that's a different story...

Perhaps a complete example would help illustrate the idea I had in mind. First lets define the code on the MATLAB side:

GlobalData.m

This is the handle class used to store the global vars.

classdef GlobalData < handle
    %GLOBALDATA  Handle class to encapsulate all global state data.
    %
    % Note that we are not taking advantage of any object-oriented programming
    % concept in this code. This class acts only as a container for publicly
    % accessible properties for the otherwise global variables.
    %
    % To manipulate these globals from C++, you should create the class API
    % as normal MATLAB functions to be compiled and exposed as regular C
    % functions by the shared library.
    % For example: create(), get(), set(), ...
    %
    % The reason we use a handle-class instead of regular variables/structs
    % is that handle-class objects get passed by reference.
    %

    properties
        val
    end
end

create_globals.m

A wrapper function that acts as a constructor to the above class

function globals = create_globals()
    %CREATE_GLOBALS  Instantiate and return global state

    globals = GlobalData();
    globals.val = 2;
end

fcn_add.m, fcn_times.m

MATLAB functions to be exposed as C++ functions

function out = fcn_add(globals, in)
    % receives array, and return "input+val" (where val is global)

    out = in + globals.val;
end

function out = fcn_times(globals, in)
    % receives array, and return "input*val" (where val is global)

    out = in .* globals.val;
end

With the above files stored in current directory, lets build the C++ shared library using the MATLAB Compiler:

>> mkdir out
>> mcc -W cpplib:libfoo -T link:lib -N -v -d ./out create_globals.m fcn_add.m fcn_times.m

You should expect the following generated files among others (I'm on a Windows machine):

./out/libfoo.h
./out/libfoo.dll
./out/libfoo.lib

Next, we could create a sample C++ program to test the library:

main.cpp

// Sample program that calls a C++ shared library created using
// the MATLAB Compiler.

#include <iostream>
using namespace std;

// include library header generated by MATLAB Compiler
#include "libfoo.h"

int run_main(int argc, char **argv)
{
    // initialize MCR
    if (!mclInitializeApplication(NULL,0)) {
        cerr << "Failed to init MCR" << endl;
        return -1;
    }

    // initialize our library
    if( !libfooInitialize() ) {
        cerr << "Failed to init library" << endl;
        return -1;
    }

    try {
        // create global variables
        mwArray globals;
        create_globals(1, globals);

        // create input array
        double data[] = {1,2,3,4,5,6,7,8,9};
        mwArray in(3, 3, mxDOUBLE_CLASS, mxREAL);
        in.SetData(data, 9);

        // create output array, and call library functions
        mwArray out;
        fcn_add(1, out, globals, in);
        cout << "Added matrix:\n" << out << endl;
        fcn_times(1, out, globals, in);
        cout << "Multiplied matrix:\n" << out << endl;
    } catch (const mwException& e) {
        cerr << e.what() << endl;
        return -1;
    } catch (...) {
        cerr << "Unexpected error thrown" << endl;
        return -1;
    }

    // destruct our library
    libfooTerminate();

    // shutdown MCR
    mclTerminateApplication();

    return 0;
}

int main()
{
    mclmcrInitialize();
    return mclRunMain((mclMainFcnType)run_main, 0, NULL);
}

Lets build the standalone program:

>> mbuild -I./out main.cpp ./out/libfoo.lib -outdir ./out

And finally run the executable:

>> cd out
>> !main
Added matrix: 
     3     6     9 
     4     7    10 
     5     8    11 
Multiplied matrix: 
     2     8    14 
     4    10    16 
     6    12    18 

HTH

like image 68
Amro Avatar answered Feb 23 '23 04:02

Amro


Following on from the thread in the previous post, the suggestion wasn't to wrap your functions in a class, but rather to use a class to pass about the global variable which compiling leaves you unable to use.

classdef Foo < handle
  properties
    value
  end

  methods
    function obj = Foo(value)
      obj.value = value;
    end
  end
end

Note: the class Foo extends the handle class in order to make it pass by reference, rather than pass by value. See: the comparison between handle and value classes.

function foo = matlabA()
  foo = new Foo(1);
end

function matlabB(foo)
  foo.value
end

As far as I know, the matlab compiler doesn't compile the code as such, but rather packages it with a copy of the MATLAB Component Runtime and writes some wrapper functions to handle invoking said runtime on the code from c/c++.

I would recommend avoiding jumping back and forth between matlab and c/c++ too much; there is bound to be some overhead to converting the datatypes and invoking the MCR. All I really use it for is wrapping up a complex but self-contained matlab script (i.e.: doesn't need to interact with the c/c++ code mid way through said script) as a function, or packaging up code for deployment to environments which don't have a full copy of matlab.

As an interesting side note: if you are calling C++ from within Matlab, and that C++ code needs access to a global variable, things are much easier. You can simply do this by wrapping your C++ code into a mexFunction and compiling that. In the places you need to access a variable which is in the Matlab workspace, you can do so using the mexGetVariablePtr which will return a read-only pointer to the data. The variable you are accessing can be in either the global workspace, or that of the function which called your mexFunction.

With this approach I would suggest liberally comment the variable that you are getting in both the C++ and Matlab code, as the link between them may not be obvious from the Matlab side; you wouldn't want someone to come along later, edit the script and wonder why it had broken.

In this case it seems that the C++ side doesn't really need access to the data, so you could refactor it to have matlab do the calling by wrapping the "get current position of fingers" code into a mexFunction, then have matlab do the loop:

data = loadData();
while(~stop) {
  position = getFingerPositionMex();
  soundByCoef(position, data);
}

Assuming you don't modify the data within soundByCoef Matlab will use pass by reference, so there will be no copying of the large dataset.

like image 34
Alan Avatar answered Feb 23 '23 04:02

Alan