diff --git a/ortools/linear_solver/xpress_interface.cc b/ortools/linear_solver/xpress_interface.cc index b707c15a7e8..fdf92a5bc74 100644 --- a/ortools/linear_solver/xpress_interface.cc +++ b/ortools/linear_solver/xpress_interface.cc @@ -263,8 +263,8 @@ class MPCallbackWrapper { explicit MPCallbackWrapper(MPCallback* callback) : callback_(callback){}; MPCallback* GetCallback() const { return callback_; } // Since our (C++) call-back functions are called from the XPRESS (C) code, - // exceptions thrown in our call-back code are not caught by XPRESS. - // We have to catch them, interrupt XPRESS, and re-throw them after XPRESS is + // exceptions thrown in our call-back code are not caught by XPRESS. + // We have to catch them, interrupt XPRESS, and log them after XPRESS is // effectively interrupted (ie after solve). void CatchException(XPRSprob cbprob) { exceptions_mutex_.lock(); @@ -272,13 +272,17 @@ class MPCallbackWrapper { interruptXPRESS(cbprob, CALLBACK_EXCEPTION); exceptions_mutex_.unlock(); } - void RethrowCaughtExceptions() { + void LogCaughtExceptions() { exceptions_mutex_.lock(); for (const std::exception_ptr& ex : caught_exceptions_) { try { std::rethrow_exception(ex); - } catch (std::bad_exception const&) { - LOG(ERROR) << "Bad exception"; + } catch (std::exception &ex) { + // We don't want the interface to throw exceptions, plus it causes + // SWIG issues in Java & Python. Instead, we'll only log them. + // (The use cases where the user has to raise an exception inside their + // call-back does not seem to be frequent, anyway.) + LOG(ERROR) << "Caught exception during user-defined call-back: " << ex.what(); } } caught_exceptions_.clear(); @@ -1904,7 +1908,7 @@ MPSolver::ResultStatus XpressInterface::Solve(MPSolverParameters const& param) { } if (mp_callback_wrapper != nullptr) { - mp_callback_wrapper->RethrowCaughtExceptions(); + mp_callback_wrapper->LogCaughtExceptions(); delete mp_callback_wrapper; } diff --git a/ortools/linear_solver/xpress_interface_test.cc b/ortools/linear_solver/xpress_interface_test.cc index 184fe4e3596..2c251824f66 100644 --- a/ortools/linear_solver/xpress_interface_test.cc +++ b/ortools/linear_solver/xpress_interface_test.cc @@ -1333,22 +1333,17 @@ TEST(XpressInterface, SetAndResetCallBack) { } TEST(XpressInterface, CallbackThrowsException) { - // Test that when the callback throws an exception, it is caught and re-thrown + // Test that when the callback throws an exception, it is caught and logged UNITTEST_INIT_MIP(); auto oldMpCallback = buildLargeMipWithCallback(solver, 30, 30); auto newMpCallback = new MyMPCallback(&solver, true); solver.SetCallback((MPCallback*)newMpCallback); - EXPECT_THROW( - { - try { - solver.Solve(); - } catch (const std::runtime_error& e) { - // this tests that it has the correct message - EXPECT_STREQ("This is a mocked exception in MyMPCallback", e.what()); - throw; - } - }, - std::runtime_error); + testing::internal::CaptureStderr(); + EXPECT_NO_THROW(solver.Solve()); + std::string errors = testing::internal::GetCapturedStderr(); + // Test that StdErr contains the following error message + std::string expected_error = "Caught exception during user-defined call-back: This is a mocked exception in MyMPCallback"; + ASSERT_NE(errors.find(expected_error), std::string::npos); } } // namespace operations_research