From fe3cf386c3d6b18128ec036525ebca47d86192fe Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Tue, 13 Aug 2002 00:45:09 +0000 Subject: [PATCH] Python->C++ exception translation [SVN r14800] --- .../boost/python/detail/exception_handler.hpp | 48 +++++++++++++++++++ .../python/detail/translate_exception.hpp | 46 ++++++++++++++++++ include/boost/python/errors.hpp | 2 +- include/boost/python/exception_translator.hpp | 25 ++++++++++ src/errors.cpp | 45 ++++++++++++++++- test/Jamfile | 1 + test/exception_translator.cpp | 25 ++++++++++ test/exception_translator.py | 22 +++++++++ 8 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 include/boost/python/detail/exception_handler.hpp create mode 100644 include/boost/python/detail/translate_exception.hpp create mode 100644 include/boost/python/exception_translator.hpp create mode 100644 test/exception_translator.cpp create mode 100644 test/exception_translator.py diff --git a/include/boost/python/detail/exception_handler.hpp b/include/boost/python/detail/exception_handler.hpp new file mode 100644 index 00000000..9a9aa4d2 --- /dev/null +++ b/include/boost/python/detail/exception_handler.hpp @@ -0,0 +1,48 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef EXCEPTION_HANDLER_DWA2002810_HPP +# define EXCEPTION_HANDLER_DWA2002810_HPP + +# include +# include + +namespace boost { namespace python { namespace detail { + +struct BOOST_PYTHON_DECL exception_handler; + +typedef function2 const&> handler_function; + +struct BOOST_PYTHON_DECL exception_handler +{ + private: // types + + public: + explicit exception_handler(handler_function const& impl); + + inline bool handle(function0 const& f) const; + + bool operator()(function0 const& f) const; + + static exception_handler* chain; + + private: + static exception_handler* tail; + + handler_function m_impl; + exception_handler* m_next; +}; + + +inline bool exception_handler::handle(function0 const& f) const +{ + return this->m_impl(*this, f); +} + +BOOST_PYTHON_DECL void register_exception_handler(handler_function const& f); + +}}} // namespace boost::python::detail + +#endif // EXCEPTION_HANDLER_DWA2002810_HPP diff --git a/include/boost/python/detail/translate_exception.hpp b/include/boost/python/detail/translate_exception.hpp new file mode 100644 index 00000000..ea4d5056 --- /dev/null +++ b/include/boost/python/detail/translate_exception.hpp @@ -0,0 +1,46 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef TRANSLATE_EXCEPTION_DWA2002810_HPP +# define TRANSLATE_EXCEPTION_DWA2002810_HPP + +# include +# include + +namespace boost { namespace python { namespace detail { + +struct exception_handler; + +// A ternary function object used to translate C++ exceptions of type +// ExceptionType into Python exceptions by invoking an object of type +// Translate. Typically the translate function will be curried with +// boost::bind(). +template +struct translate_exception +{ + typedef typename add_reference< + typename add_const::type + >::type exception_cref; + + inline bool operator()( + exception_handler const& handler + , function0 const& f + , typename call_traits::param_type translate) const + { + try + { + return handler(f); + } + catch(exception_cref e) + { + translate(e); + return true; + } + } +}; + +}}} // namespace boost::python::detail + +#endif // TRANSLATE_EXCEPTION_DWA2002810_HPP diff --git a/include/boost/python/errors.hpp b/include/boost/python/errors.hpp index 4c47aca8..390f3b61 100644 --- a/include/boost/python/errors.hpp +++ b/include/boost/python/errors.hpp @@ -20,7 +20,7 @@ struct BOOST_PYTHON_DECL argument_error : error_already_set {}; // Handles exceptions caught just before returning to Python code. // Returns true iff an exception was caught. -BOOST_PYTHON_DECL bool handle_exception_impl(function0); +BOOST_PYTHON_DECL bool handle_exception_impl(function0 const&); template bool handle_exception(T f) diff --git a/include/boost/python/exception_translator.hpp b/include/boost/python/exception_translator.hpp new file mode 100644 index 00000000..830d50d2 --- /dev/null +++ b/include/boost/python/exception_translator.hpp @@ -0,0 +1,25 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. +#ifndef EXCEPTION_TRANSLATOR_DWA2002810_HPP +# define EXCEPTION_TRANSLATOR_DWA2002810_HPP +# include +# include +# include +# include + +namespace boost { namespace python { + +template +void register_exception_translator(Translate const& translate, boost::type* = 0) +{ + detail::register_exception_handler( + bind(detail::translate_exception(), _1, _2, translate) + ); +} + +}} // namespace boost::python + +#endif // EXCEPTION_TRANSLATOR_DWA2002810_HPP diff --git a/src/errors.cpp b/src/errors.cpp index cd188cb6..882d2917 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -10,14 +10,21 @@ #include #include +#ifdef BOOST_PYTHON_V2 +# include +#endif namespace boost { namespace python { // IMPORTANT: this function may only be called from within a catch block! -BOOST_PYTHON_DECL bool handle_exception_impl(function0 f) +BOOST_PYTHON_DECL bool handle_exception_impl(function0 const& f) { try { +#ifdef BOOST_PYTHON_V2 + if (detail::exception_handler::chain) + return detail::exception_handler::chain->handle(f); +#endif f(); return false; } @@ -68,6 +75,42 @@ namespace detail { // needed by void_adaptor (see void_adaptor.hpp) BOOST_PYTHON_DECL PyObject arbitrary_object = { 0 }; +#ifdef BOOST_PYTHON_V2 +bool exception_handler::operator()(function0 const& f) const +{ + if (m_next) + { + return m_next->handle(f); + } + else + { + f(); + return false; + } +} + +exception_handler::exception_handler(handler_function const& impl) + : m_impl(impl) + , m_next(0) +{ + if (chain != 0) + tail->m_next = this; + else + chain = this; + tail = this; +} + +exception_handler* exception_handler::chain; +exception_handler* exception_handler::tail; + +BOOST_PYTHON_DECL void register_exception_handler(handler_function const& f) +{ + // the constructor links the new object into a handler chain, so + // this object isn't actaully leaked (until, of course, the + // interpreter exits). + new exception_handler(f); +} +#endif } // namespace boost::python::detail diff --git a/test/Jamfile b/test/Jamfile index 0752ddc2..eb5254d2 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -56,6 +56,7 @@ rule bpl-test ( name ? : files * ) bpl-test minimal ; bpl-test docstring ; +bpl-test exception_translator ; bpl-test pearu1 : test_cltree.py cltree.cpp ; bpl-test try : newtest.py m1.cpp m2.cpp ; bpl-test builtin_converters : test_builtin_converters.py test_builtin_converters.cpp ; diff --git a/test/exception_translator.cpp b/test/exception_translator.cpp new file mode 100644 index 00000000..58512ef2 --- /dev/null +++ b/test/exception_translator.cpp @@ -0,0 +1,25 @@ +#include +#include + +struct error {}; + +void translate(error const& e) +{ + PyErr_SetString(PyExc_RuntimeError, "!!!error!!!"); +} + +void throw_error() +{ + throw error(); + +} + +BOOST_PYTHON_MODULE_INIT(exception_translator_ext) +{ + using namespace boost::python; + + register_exception_translator(&translate); + module("exception_translator_ext") + .def("throw_error", throw_error); +} + diff --git a/test/exception_translator.py b/test/exception_translator.py new file mode 100644 index 00000000..78fe3507 --- /dev/null +++ b/test/exception_translator.py @@ -0,0 +1,22 @@ +''' +>>> from exception_translator_ext import * +>>> try: +... throw_error(); +... except RuntimeError, x: +... print x +... else: +... print 'Expected a RuntimeError!' +!!!error!!! +''' +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + sys.exit(run()[0])