#pragma once #define MEX_DOUBLE_HANDLE #include #include #include #include #include #define ENABLE_MATLAB inline void ensure(bool cond, const char *msg, ...) { if (!cond) { va_list args; va_start(args, msg); vfprintf(stderr, (msg+std::string("\n")).c_str(), args); va_end(args); } } #define ensureTypeMatch(R, m, othertype) ensure(MatlabNum::id == mxGetClassID(m), "Matlab type does not match "##othertype) class MatlabEngine { public: bool consoleOutput; MatlabEngine():eng(nullptr), consoleOutput(true) { } virtual ~MatlabEngine() { if(eng) close(); } // Run inside matlab: enableservice('AutomationServer', true) bool connect(const std::string &dir, bool closeAll=false); bool connected() const { return eng!=nullptr; } void setEnable(bool v) { if (v) connect(""); else close(); } void eval(const std::string &cmd, bool PrintInfo = true); void close(); void hold_on() { eval("hold on;"); } void hold_off() { eval("hold off;"); } const char *output_buffer() { return (*engBuffer)?engBuffer:nullptr; } bool hasVar(const std::string &name) { ensure(connected(), "Not connected to Matlab!"); mxArray *m = engGetVariable(eng, name.c_str()); bool r = (m != nullptr); mxDestroyArray(m); return r; } mxArray* getVariable(const std::string &name) { ensure(connected(), "Not connected to Matlab!"); mxArray *m = engGetVariable(eng, name.c_str()); ensure(m!=nullptr, "Matlab doesn't have a variable: %s\n", name.c_str()); return m; } int putVariable(const std::string &name, const mxArray *m) { ensure(connected(), "Not connected to Matlab!"); return engPutVariable(eng, name.c_str(), m); } private: Engine *eng; // Matlab engine static const int lenEngBuffer = 1000000; char engBuffer[lenEngBuffer]; // engine buffer for outputting strings }; MatlabEngine& getMatEngine(); // inline void matlabEval(const char* cmd) { getMatEngine().eval(cmd); } inline void matlabEval(const std::string &cmd, bool PrintInfo = true) { getMatEngine().eval(cmd.c_str(), PrintInfo); } inline bool matEngineConnected() { return getMatEngine().connected(); } template struct MatlabNum { static const mxClassID id = mxUNKNOWN_CLASS; }; template<> struct MatlabNum { static const mxClassID id = mxLOGICAL_CLASS; }; template<> struct MatlabNum { static const mxClassID id = mxCHAR_CLASS; }; template<> struct MatlabNum { static const mxClassID id = mxINT32_CLASS; }; template<> struct MatlabNum { static const mxClassID id = mxSINGLE_CLASS; }; template<> struct MatlabNum{ static const mxClassID id = mxDOUBLE_CLASS; }; template inline mxArray* createMatlabArray(const mwSize *dims, int ndim) { return mxCreateNumericArray(ndim, dims, MatlabNum::id, mxREAL); } //////////////////////////////////////////////////////////////////////////////////////////////////// template void VecVec2IdxVec(const std::vector >& in, std::vector& v, std::vector& idx) { const size_t nvec = in.size(); idx.resize(nvec+1); idx[0] = 0; size_t n = 0; for(size_t i=0; i std::vector > IdxVec2VecVec(const std::vector& vcat, const std::vector& vidx) { ensure(!vidx.empty(), "empty indices"); const size_t nvec = vidx.size()-1; std::vector > v(nvec); for(size_t i=0; i(vcat.begin()+vidx[i], vcat.begin()+vidx[i+1]); return v; } //////////////////////////////////////////////////////////////////////////////////////////////////// template void vector2matlab(const std::vector &v, mxArray *m) { ensureTypeMatch(R, m, "std::vector"); R *pm = (R *)mxGetData(m); for ( unsigned i = 0 ; i < v.size() ; i++ ) pm[i] = (R)v[i]; } template void vector2matlab(const std::string &name , const std::vector &v) { mwSize dim[] = { v.size() }; mxArray *m = createMatlabArray(dim, 1); vector2matlab(v, m); getMatEngine().putVariable(name, m); mxDestroyArray(m); } template std::vector matlab2vector(const mxArray *m) { ensureTypeMatch(R, m, "std::vector"); if (mxIsSparse(m)){ ensure(false, "matrix is sparse!"); return std::vector(); } const R *pm = (R*)mxGetData(m); return std::vector( pm, pm + mxGetNumberOfElements(m) ); } inline std::string matlab2string(const std::string &name) { mxArray *m = getMatEngine().getVariable(name); //std::wstring str(mxGetChars(m)); if (!m) return std::string(); size_t len = mxGetNumberOfElements(m) + 1; std::vector str(len); mxGetString(m, str.data(), len); mxDestroyArray(m); return std::string(str.cbegin(), str.cend()-1); } inline std::vector matlab2strings(const std::string &name) { mxArray *m = getMatEngine().getVariable(name); std::vector strs; if (!m) return strs; for (int i = 0; i < mxGetNumberOfElements(m); i++) { mxArray *mstr = mxGetCell(m, i); size_t len = mxGetNumberOfElements(mstr) + 1; // for \0 end std::vector str(len); mxGetString(mstr, str.data(), len); strs.push_back(std::string(str.cbegin(), str.cend()-1)); } mxDestroyArray(m); return strs; } inline void string2matlab(const std::string &name, const std::string &val) { mxArray *m = mxCreateString(val.c_str()); getMatEngine().putVariable(name, m); mxDestroyArray(m); } template std::vector matlab2vector(const std::string &name, bool temp=false) { const std::string tempname("mytempval4c"); if (temp) getMatEngine().eval(tempname + "=" + name + ";"); mxArray *m = getMatEngine().getVariable(temp?tempname:name); if (!m) return std::vector(); std::vector v = matlab2vector(m); mxDestroyArray(m); if (temp) getMatEngine().eval( "clear " + tempname + ";"); return v; } //////////////////////////////////////////////////////////////////////////////////////////////////// template void vecvec2matlabcell(const std::string &name, const std::vector > &v) { std::vector vcat, vidx; VecVec2IdxVec(v, vcat, vidx); vector2matlab("vcat_tmp", vcat); vector2matlab("vidx_tmp", vidx); std::stringstream ss; ss< void matlabcell2idxvec(const std::string &name, std::vector &vcat, std::vector &vidx) { std::stringstream ss; ss<<"[vcat_tmp vidx_tmp]=cell2indexedArray("<("vcat_tmp"); vidx = matlab2vector("vidx_tmp"); matlabEval( "clear vcat_tmp vidx_tmp;" ); } template std::vector > matlabcell2vecvec(const std::string &name) { std::vector vcat, vidx; matlabcell2vecvec(name, vcat, vidx); return IdxVec2VecVec(vcat, vidx); } ////////////////////////////////////////////////////////////////////////////////////////////////////// //template //void matlab2eigen(const mxArray *m , Eigen::MatrixBase &v) //{ // typedef M::Scalar R; // const mwSize *dim = mxGetDimensions(m); // const R *pm = (R*)mxGetData(m); // // ensure(dim[0]==v.rows() && dim[1]==v.cols()); // //v.resize(dim[0], dim[1]); // // for ( unsigned i = 0 ; i < dim[0] ; i++ ) // for ( unsigned j = 0 ; j < dim[1] ; j++ ) { // const mwSize ind2[] = {i, j}; // v(i,j) = pm[mxCalcSingleSubscript(m, 2, ind2)]; // } //} // //template //void matlab2eigen(const std::string &name, Eigen::MatrixBase &v) //{ // mxArray *m = getMatEngine().getVariable(name); // if ( !m ) return; // // matlab2eigen(m, v); // // mxDestroyArray(m); //} template void matlab2eigen(const mxArray *m , EigenMatrix &v) { typedef typename EigenMatrix::Scalar R; ensureTypeMatch(R, m, "Eigen::Matrix"); if (mxIsSparse(m)){ ensure(false, "matrix is sparse!"); return; } const mwSize *dim = mxGetDimensions(m); v = Eigen::Map >((R*)mxGetData(m), dim[0], dim[1]); } template void matlab2eigen(const std::string &name, Matrix &v, bool temp=false) { const std::string tempname("mytempval4c"); if (temp) getMatEngine().eval(tempname + "=" + name + ";"); mxArray *m = getMatEngine().getVariable(temp?tempname:name); if (!m) return; matlab2eigen(m, v); mxDestroyArray(m); if (temp) getMatEngine().eval( "clear " + tempname + ";"); } inline Eigen::MatrixXcd matlab2eigenComplex(const std::string &name) { mxArray *m = getMatEngine().getVariable(name); if (!m) return Eigen::MatrixXcd(); ensure(!mxIsSparse(m), "matrix is sparse!"); const mwSize *dim = mxGetDimensions(m); Eigen::MatrixXcd v(dim[0], dim[1]); v.real() = Eigen::Map >(mxGetPr(m), dim[0], dim[1]); if (mxIsComplex(m)) v.imag() = Eigen::Map >(mxGetPi(m), dim[0], dim[1]); else v.imag().setZero(); mxDestroyArray(m); return v; } template inline void eigen2matlabComplex(const std::string &name, const Mat &vr, const Mat &vi) { mwSize dim[] = { vr.rows(), vr.cols() }; mxArray *m = mxCreateNumericArray(2, dim, MatlabNum::id, mxCOMPLEX); using MapMat = Eigen::Map < Eigen::Matrix > ; MapMat(mxGetPr(m), dim[0], dim[1]) = vr; MapMat(mxGetPi(m), dim[0], dim[1]) = vi; getMatEngine().putVariable(name, m); mxDestroyArray(m); } template void eigen2matlab(const Eigen::MatrixBase &v, mxArray *m) { typedef M::Scalar R; ensureTypeMatch(R, m, "Eigen::Matrix"); using namespace Eigen; Map >((R*)mxGetData(m), v.rows(), v.cols()) = v; } inline void scalar2matlab(const std::string &name, double v) { mxArray *m = mxCreateDoubleScalar(v); getMatEngine().putVariable(name, m); mxDestroyArray(m); } inline double matlab2scalar(const std::string &name, double fallback=0, bool temp=false) { const std::string tempname("mytempval4c"); auto &eng = getMatEngine(); if (temp) eng.eval(tempname + "=" + name + ";"); if (!eng.hasVar(temp ? tempname : name)) return fallback; mxArray *m = eng.getVariable(temp?tempname:name); ensure(mxIsScalar(m), "Matlab: %s is not a scalar!", name.c_str()); double r = (m && mxIsScalar(m))?mxGetScalar(m):fallback; mxDestroyArray(m); if (temp) eng.eval( "clear " + tempname + ";"); return r; } inline bool matlab2bool(const std::string& name, bool fallback = false, bool temp = false) { const std::string tempname("mytempval4c"); auto& eng = getMatEngine(); if (temp) eng.eval(tempname + "=" + name + ";"); if (!eng.hasVar(temp ? tempname : name)) return fallback; mxArray* m = eng.getVariable(temp ? tempname : name); bool r = *(bool*)mxGetData(m); mxDestroyArray(m); if (temp) eng.eval("clear " + tempname + ";"); return r; } template void eigen2matlab(const std::string &name, const Eigen::MatrixBase &v) { typedef typename M::Scalar R; mwSize dim[] = { mwSize(v.rows()), mwSize(v.cols()) }; mxArray *m = createMatlabArray(dim, 2); eigen2matlab(v, m); getMatEngine().putVariable(name, m); mxDestroyArray(m); } template void eigen2matlab(const std::string &name, const Eigen::SparseMatrix &A) { int rows = A.rows(), cols = A.cols(), nnz = A.nonZeros(); mxArray *m = mxCreateSparse(rows, cols, nnz, mxREAL); std::copy_n(A.valuePtr(), nnz, mxGetPr(m)); std::copy_n(A.outerIndexPtr(), rows+1, mxGetJc(m)); std::copy_n(A.innerIndexPtr(), nnz, mxGetIr(m)); getMatEngine().putVariable(name, m); mxDestroyArray(m); }