diff --git a/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc b/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc index 4ed7b9be9ed2f8b99c8d8db1a5635f3c766edb1f..86c22c61dde40f4947f815d8bce67cb52900eff3 100644 --- a/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc +++ b/mex/sources/perfect_foresight_problem/DynamicModelCaller.cc @@ -26,6 +26,7 @@ using namespace std::literals::string_literals; std::string DynamicModelCaller::error_msg; +std::string DynamicModelCaller::error_id; std::mutex DynamicModelCaller::error_mtx; #if !defined(_WIN32) && !defined(__CYGWIN32__) @@ -40,6 +41,44 @@ DynamicModelDllCaller::dynamic_tt_fct DynamicModelDllCaller::residual_tt_fct {nu DynamicModelDllCaller::dynamic_fct DynamicModelDllCaller::residual_fct {nullptr}, DynamicModelDllCaller::g1_fct {nullptr}; +void +DynamicModelCaller::setErrMsg(std::string msg) +{ + std::lock_guard lk {error_mtx}; + error_msg = move(msg); + error_id.clear(); +} + +void +DynamicModelCaller::setMException(const mxArray* exception) +{ + const mxArray* message_mx {nullptr}; + const mxArray* identifier_mx {nullptr}; + if (mxIsClass(exception, "MException")) + { + message_mx = mxGetProperty(exception, 0, "message"); + identifier_mx = mxGetProperty(exception, 0, "identifier"); + } + else if (mxIsStruct(exception)) // For Octave + { + message_mx = mxGetField(exception, 0, "message"); + identifier_mx = mxGetField(exception, 0, "identifier"); + } + else + mexErrMsgTxt("Exception of incorrect type"); + + if (!message_mx || !mxIsChar(message_mx) || !identifier_mx || !mxIsChar(identifier_mx)) + mexErrMsgTxt("Exception object malformed"); + + char* message = mxArrayToString(message_mx); + char* identifier = mxArrayToString(identifier_mx); + std::lock_guard lk {error_mtx}; + error_msg = message; + error_id = identifier; + mxFree(message); + mxFree(identifier); +} + void DynamicModelDllCaller::load_dll(const std::string& basename) { @@ -211,15 +250,13 @@ DynamicModelMatlabCaller::eval(double* resid) funcname.c_str())}; if (exception) { - std::lock_guard lk {error_mtx}; - error_msg = "An error occurred when calling " + funcname; + setMException(exception); return; // Avoid manipulating null pointers in plhs, see #1832 } if (!mxIsDouble(plhs[0]) || mxIsSparse(plhs[0])) { - std::lock_guard lk {error_mtx}; - error_msg = "Residuals should be a dense array of double floats"; + setErrMsg("Residuals should be a dense array of double floats"); return; } @@ -252,8 +289,7 @@ DynamicModelMatlabCaller::eval(double* resid) funcname.c_str())}; if (exception) { - std::lock_guard lk {error_mtx}; - error_msg = "An error occurred when calling " + funcname; + setMException(exception); return; // Avoid manipulating null pointers in plhs, see #1832 } @@ -265,8 +301,7 @@ DynamicModelMatlabCaller::eval(double* resid) if (!mxIsDouble(plhs[0]) || !mxIsSparse(plhs[0])) { - std::lock_guard lk {error_mtx}; - error_msg = "Jacobian should be a sparse array of double floats"; + setErrMsg("Residuals should be a dense array of double floats"); return; } diff --git a/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh b/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh index 09612b4d69b4a28f4b200f57fd23a0e5da147dfa..33192afd33c5bad6528a0dc348ab3cd1651e05e7 100644 --- a/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh +++ b/mex/sources/perfect_foresight_problem/DynamicModelCaller.hh @@ -48,6 +48,7 @@ public: // Used to store error messages (as exceptions cannot cross the OpenMP boundary) static std::string error_msg; + static std::string error_id; static std::mutex error_mtx; // Guard for access in OpenMP context DynamicModelCaller(bool linear_arg, bool compute_jacobian_arg) : @@ -61,6 +62,8 @@ public: Only copies non-zero elements, according to g1_sparse_{rowval,colval,colptr}. */ virtual void copy_jacobian_column(mwIndex col, double* dest) const = 0; virtual void eval(double* resid) = 0; + static void setErrMsg(std::string msg); + static void setMException(const mxArray* exception); }; class DynamicModelDllCaller : public DynamicModelCaller diff --git a/mex/sources/perfect_foresight_problem/perfect_foresight_problem.cc b/mex/sources/perfect_foresight_problem/perfect_foresight_problem.cc index 7cd7252f2039fbfe8fb528c42ead0545f66ded73..177dedd8932bc8a436c7ec381b7fc76e299e31e5 100644 --- a/mex/sources/perfect_foresight_problem/perfect_foresight_problem.cc +++ b/mex/sources/perfect_foresight_problem/perfect_foresight_problem.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2019-2024 Dynare Team + * Copyright © 2019-2025 Dynare Team * * This file is part of Dynare. * @@ -205,6 +205,7 @@ mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) DynamicModelDllCaller::load_dll(basename); DynamicModelCaller::error_msg.clear(); + DynamicModelCaller::error_id.clear(); /* Parallelize the main loop, if use_dll and no external function (to avoid parallel calls to MATLAB) */ @@ -282,7 +283,7 @@ mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) /* Mimic a try/catch using a global string, since exceptions are not allowed to cross OpenMP boundary */ if (!DynamicModelCaller::error_msg.empty()) - mexErrMsgTxt(DynamicModelCaller::error_msg.c_str()); + mexErrMsgIdAndTxt(DynamicModelCaller::error_id.c_str(), DynamicModelCaller::error_msg.c_str()); if (compute_jacobian) {