From 381b474ae8be07050367530fa28d3ce487d7f1ce Mon Sep 17 00:00:00 2001 From: Fan Jiang Date: Sat, 22 Aug 2026 18:37:18 -0700 Subject: [PATCH] Fix MATLAB MEX error handling for Octave --- gtwrap/matlab_wrapper/templates.py | 1 + matlab.h | 2 -- tests/expected/matlab/class_wrapper.cpp | 1 + tests/expected/matlab/enum_wrapper.cpp | 1 + tests/expected/matlab/functions_wrapper.cpp | 1 + tests/expected/matlab/geometry_wrapper.cpp | 1 + tests/expected/matlab/inheritance_wrapper.cpp | 1 + .../matlab/multiple_files_wrapper.cpp | 1 + tests/expected/matlab/namespaces_wrapper.cpp | 1 + .../expected/matlab/special_cases_wrapper.cpp | 1 + tests/expected/matlab/template_wrapper.cpp | 1 + tests/test_matlab_wrapper.py | 28 +++++++++++++++++++ 12 files changed, 38 insertions(+), 2 deletions(-) diff --git a/gtwrap/matlab_wrapper/templates.py b/gtwrap/matlab_wrapper/templates.py index c1c7e75c..bff16500 100644 --- a/gtwrap/matlab_wrapper/templates.py +++ b/gtwrap/matlab_wrapper/templates.py @@ -137,6 +137,7 @@ class WrapperTemplate: switch(id) {{ {cases} }} }} catch(const std::exception& e) {{ + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\\n" + std::string(e.what()) + "\\n").c_str()); }}\n std::cout.rdbuf(outbuf); diff --git a/matlab.h b/matlab.h index 37dffe4f..1347f297 100644 --- a/matlab.h +++ b/matlab.h @@ -33,9 +33,7 @@ using gtsam::Matrix; using gtsam::Point2; using gtsam::Point3; -extern "C" { #include -} #include #include diff --git a/tests/expected/matlab/class_wrapper.cpp b/tests/expected/matlab/class_wrapper.cpp index fb7499dc..29d71568 100644 --- a/tests/expected/matlab/class_wrapper.cpp +++ b/tests/expected/matlab/class_wrapper.cpp @@ -1380,6 +1380,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/enum_wrapper.cpp b/tests/expected/matlab/enum_wrapper.cpp index 637dd503..71e80759 100644 --- a/tests/expected/matlab/enum_wrapper.cpp +++ b/tests/expected/matlab/enum_wrapper.cpp @@ -315,6 +315,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/functions_wrapper.cpp b/tests/expected/matlab/functions_wrapper.cpp index 7ec9638a..e076237e 100644 --- a/tests/expected/matlab/functions_wrapper.cpp +++ b/tests/expected/matlab/functions_wrapper.cpp @@ -402,6 +402,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/geometry_wrapper.cpp b/tests/expected/matlab/geometry_wrapper.cpp index afb28803..691fddbb 100644 --- a/tests/expected/matlab/geometry_wrapper.cpp +++ b/tests/expected/matlab/geometry_wrapper.cpp @@ -400,6 +400,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/inheritance_wrapper.cpp b/tests/expected/matlab/inheritance_wrapper.cpp index ab0da2e6..cc82edaf 100644 --- a/tests/expected/matlab/inheritance_wrapper.cpp +++ b/tests/expected/matlab/inheritance_wrapper.cpp @@ -995,6 +995,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/multiple_files_wrapper.cpp b/tests/expected/matlab/multiple_files_wrapper.cpp index 548fd2ab..00e141a3 100644 --- a/tests/expected/matlab/multiple_files_wrapper.cpp +++ b/tests/expected/matlab/multiple_files_wrapper.cpp @@ -218,6 +218,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/namespaces_wrapper.cpp b/tests/expected/matlab/namespaces_wrapper.cpp index 9c4be9ec..2e327ec0 100644 --- a/tests/expected/matlab/namespaces_wrapper.cpp +++ b/tests/expected/matlab/namespaces_wrapper.cpp @@ -538,6 +538,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/special_cases_wrapper.cpp b/tests/expected/matlab/special_cases_wrapper.cpp index ec8bb1b6..1cc85791 100644 --- a/tests/expected/matlab/special_cases_wrapper.cpp +++ b/tests/expected/matlab/special_cases_wrapper.cpp @@ -268,6 +268,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/expected/matlab/template_wrapper.cpp b/tests/expected/matlab/template_wrapper.cpp index a65fdc92..057e26ff 100644 --- a/tests/expected/matlab/template_wrapper.cpp +++ b/tests/expected/matlab/template_wrapper.cpp @@ -214,6 +214,7 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) break; } } catch(const std::exception& e) { + std::cout.rdbuf(outbuf); mexErrMsgTxt(("Exception from gtsam:\n" + std::string(e.what()) + "\n").c_str()); } diff --git a/tests/test_matlab_wrapper.py b/tests/test_matlab_wrapper.py index 35e243d5..f30da07c 100644 --- a/tests/test_matlab_wrapper.py +++ b/tests/test_matlab_wrapper.py @@ -480,6 +480,34 @@ def test_deconstructors_are_idempotent(self): ' }\n' ' delete self;', cpp_content) + def test_mex_error_path_restores_stream(self): + """MEX failures restore std::cout before leaving the gateway.""" + file = osp.join(self.INTERFACE_DIR, 'functions.i') + wrapper = MatlabWrapper( + module_name='functions', + top_module_namespace=['gtsam'], + ignore_classes=[''], + ) + wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) + + cpp_file = osp.join(self.MATLAB_ACTUAL_DIR, + 'functions_wrapper.cpp') + with open(cpp_file, 'r', encoding='UTF-8') as generated_file: + cpp_content = generated_file.read() + + self.assertIn( + '} catch(const std::exception& e) {\n' + ' std::cout.rdbuf(outbuf);\n' + ' mexErrMsgTxt(', cpp_content) + + matlab_header = osp.join(self.TEST_DIR, '..', 'matlab.h') + with open(matlab_header, 'r', encoding='UTF-8') as header_file: + header_content = header_file.read() + + self.assertIn('#include ', header_content) + self.assertNotIn('extern "C" {\n#include \n}', + header_content) + def test_size_t_round_trip(self): """Generated size_t wrappers use alias-safe scalar conversions.""" file = osp.join(self.INTERFACE_DIR, 'matlab_integer_aliases.i')