【问题标题】:Persist C++ class instance through Matlab mex function calls通过 Matlab mex 函数调用持久化 C++ 类实例
【发布时间】:2018-02-09 21:19:26
【问题描述】:

目前正在尝试找出解决此问题的方法。

现在我正在尝试为 C++ 库创建 mex 函数,该库通过 Windows 10 PC 上的串行端口与微控制器通信,以便我可以在 matlab 中调用该库中的函数。我目前正在研究如何通过多个 matlab mexFunction 调用来持久化我的类的实例。

到目前为止,我唯一能想到的就是为类编写一个包装器,声明一个指向我的类实例的全局 extern 唯一指针,并将它包含在我的 mexFunction() 文件中。

谁能告诉我这是否可行,如果可行,matlab/C++ 究竟如何处理 mexFunction 文件及其方法调用?我的类实例的范围是我不确定的。

一个具体的例子可能是......

如果我在 .cpp 文件中声明一个指向对象的 extern 唯一指针并将其包含在我的 mexFunction 文件中,会发生什么情况?指针是否会在调用多个不同 mexFunction 以操纵该对象的 matlab 脚本的整个范围内保持不变?

如果我需要重新表述问题或提供更多信息,请告诉我。

【问题讨论】:

    标签: c++ matlab pointers scope mex


    【解决方案1】:

    是的,您可以这样做。如果 MEX 文件都链接到同一个共享库 (DLL),那么它们都可以访问其中定义的全局变量。您需要在共享库中定义全局对象,而不是在 MEX 文件之一中。

    MEX 文件在首次执行后一直加载到内存中,直到您调用 clear functions(或 clear all)。当共享对象从内存中清除时,全局对象将被破坏。为防止意外清除您的状态,您可以使用 mexLock 锁定内存中的 MEX 文件之一。我建议使用一个“初始化” MEX 文件,该文件构造对象并将自身锁定在内存中。使用特殊参数,您可以使其自行解锁并销毁对象。


    这是一个例子:

    • libXYZ.dylib / libXYZ.so / XYZ.dll -- 一个共享库,包含一个std::shared_ptr<XYZ>

    • XYZ_set.mex... -- 一个 MEX 文件,用于初始化 XYZ 对象,并将自身锁定在内存中。指向libXYZ 共享库的链接。

    • XYZ_get.mex... -- 另一个 MEX 文件,它链接到 libXYZ 共享库并访问由另一个 MEX 文件创建的 XYZ 对象。

    XYZ_lib.h:

    #include <memory>
    #include <iostream>
    
    struct XYZ {
       XYZ(double a);
       ~XYZ();
       double get();
    private:
       double a_;
    };
    
    extern std::unique_ptr<XYZ> XYZ_data;
    

    XYZ_lib.cpp:

    #include "XYZ_lib.h"
    
    std::unique_ptr<XYZ> XYZ_data;
    
    XYZ::XYZ(double a) : a_(a) { 
       std::cout << "Constructing XYZ with " << a_ << '\n'; 
    }
    
    XYZ::~XYZ() { 
       std::cout << "Destructing XYZ, value was " << a_ << '\n'; 
    }
    
    double XYZ::get() { 
       return a_; 
    }
    

    XYZ_set.cpp:

    #include "XYZ_lib.h"
    #include <mex.h>
    
    /// \brief An output stream buffer for MEX-files.
    ///
    /// Creating an object of this class replaces the stream buffer in `std::cout` with the newly
    /// created object. This buffer will be used as long as the object exists. When the object
    /// is destroyed (which happens automatically when it goes out of scope), the original
    /// stream buffer is replaced.
    ///
    /// Create an object of this class at the beginning of any MEX-file that uses `std::cout` to
    /// print information to the *MATLAB* terminal.
    class streambuf : public std::streambuf {
       public:
          streambuf() {
             stdoutbuf = std::cout.rdbuf( this );
          }
          ~streambuf() {
             std::cout.rdbuf( stdoutbuf );
          }
       protected:
          virtual std::streamsize xsputn( const char* s, std::streamsize n ) override {
             mexPrintf( "%.*s", n, s );
             return n;
          }
          virtual int overflow( int c = EOF ) override {
             if( c != EOF ) {
                mexPrintf( "%.1s", &c );
             }
             return 1;
          }
       private:
          std::streambuf* stdoutbuf;
    };
    
    void mexFunction( int, mxArray*[], int nrhs, const mxArray* prhs[] ) {
       streambuf buf; // Allows std::cout to work in MEX-files
       // Always do lots of testing for correct input in MEX-files!
       if (nrhs!=1) {
          mexErrMsgTxt("Requires 1 input");
       }
       if (mxIsChar(prhs[0])) {
          // Assume it's "-unlock" or something like that. Unlock MEX-file
          mexUnlock();
          std::cout << "XYZ can now be cleared from memory\n";
       } else {
          // Here we create new data
          if (!mxIsDouble(prhs[0]) || mxIsEmpty(prhs[0])) {
             mexErrMsgTxt("Expected double input");
          }
          double a = *mxGetPr(prhs[0]);
          XYZ_data = std::unique_ptr<XYZ>(new XYZ(a));
          // If the MEX-file is not locked, lock it
          if (!mexIsLocked()) {
             mexLock();
          }
       }
    }
    

    (对不起这里的streambuf 类,它是噪音,但我想使用它,以便您可以看到共享库中的构造函数和析构函数被调用。)

    XYZ_get.cpp:

    #include "XYZ_lib.h"
    #include <mex.h>
    
    void mexFunction( int, mxArray* plhs[], int, const mxArray* [] ) {
       if (XYZ_data) {
          plhs[0] = mxCreateDoubleScalar(XYZ_data->get());
       } else {
          mexErrMsgTxt("XYZ not initialized!");
       }
    }
    

    编译:

    在 shell 中(我使用的是 MacOS,因此使用了 dylib 扩展名,根据需要进行调整):

    g++ -std=c++11 -Wall -fpic XYZ_lib.cpp -shared -o libXYZ.dylib
    

    在 MATLAB 中:

    mex XYZ_set.cpp libXYZ.dylib
    mex XYZ_get.cpp libXYZ.dylib
    

    跑步:

    >> XYZ_get
    Error using XYZ_get
    XYZ not initialized!
    
    >> XYZ_set(4)
    Constructing XYZ with 4
    >> XYZ_set(6)
    Constructing XYZ with 6
    Destructing XYZ, value was 4
    >> XYZ_get
    ans =
         6
    >> clear all
    >> XYZ_set -unlock
    XYZ can now be cleared from memory
    >> clear all
    Destructing XYZ, value was 6
    

    如您所见,XYZ_get 访问由newXYZ_set 编辑的对象中的值。 clear all 通常会从内存中清除所有内容,但锁定的 MEX 文件会保留在这里。 XYZ_set -unlock 使用字符串参数调用它,这会导致它自行解锁。 clear all 现在也从内存中清除该 MEX 文件,现在 XYZ 对象被销毁。


    我需要在这里提一下,C++ 没有一致的 ABI,这些 MEX 文件只有在共享库使用相同的编译器编译时才会加载。

    另一种方法(通常更简单)是只创建一个 MEX 文件(与您的 C++ 代码静态链接)和一堆调用 MEX 文件的 M 文件。 M 文件提供了很好的界面(也可以进行输入检查),MEX 文件位于private/ 目录中,没有人可以弄乱它。 MEX 文件仍然可以执行锁定操作,因此它可以保留在调用之间保留的对象。

    【讨论】:

    • 该死的,谢谢老兄!这使它更加清晰。我真的很感激!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多