Skip to content

Commit fe3cf38

Browse files
committed
Python->C++ exception translation
[SVN r14800]
1 parent 0a6a213 commit fe3cf38

File tree

8 files changed

+212
-2
lines changed

8 files changed

+212
-2
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef EXCEPTION_HANDLER_DWA2002810_HPP
7+
# define EXCEPTION_HANDLER_DWA2002810_HPP
8+
9+
# include <boost/function/function0.hpp>
10+
# include <boost/function/function2.hpp>
11+
12+
namespace boost { namespace python { namespace detail {
13+
14+
struct BOOST_PYTHON_DECL exception_handler;
15+
16+
typedef function2<bool, exception_handler const&, function0<void> const&> handler_function;
17+
18+
struct BOOST_PYTHON_DECL exception_handler
19+
{
20+
private: // types
21+
22+
public:
23+
explicit exception_handler(handler_function const& impl);
24+
25+
inline bool handle(function0<void> const& f) const;
26+
27+
bool operator()(function0<void> const& f) const;
28+
29+
static exception_handler* chain;
30+
31+
private:
32+
static exception_handler* tail;
33+
34+
handler_function m_impl;
35+
exception_handler* m_next;
36+
};
37+
38+
39+
inline bool exception_handler::handle(function0<void> const& f) const
40+
{
41+
return this->m_impl(*this, f);
42+
}
43+
44+
BOOST_PYTHON_DECL void register_exception_handler(handler_function const& f);
45+
46+
}}} // namespace boost::python::detail
47+
48+
#endif // EXCEPTION_HANDLER_DWA2002810_HPP
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef TRANSLATE_EXCEPTION_DWA2002810_HPP
7+
# define TRANSLATE_EXCEPTION_DWA2002810_HPP
8+
9+
# include <boost/function/function0.hpp>
10+
# include <boost/call_traits.hpp>
11+
12+
namespace boost { namespace python { namespace detail {
13+
14+
struct exception_handler;
15+
16+
// A ternary function object used to translate C++ exceptions of type
17+
// ExceptionType into Python exceptions by invoking an object of type
18+
// Translate. Typically the translate function will be curried with
19+
// boost::bind().
20+
template <class ExceptionType, class Translate>
21+
struct translate_exception
22+
{
23+
typedef typename add_reference<
24+
typename add_const<ExceptionType>::type
25+
>::type exception_cref;
26+
27+
inline bool operator()(
28+
exception_handler const& handler
29+
, function0<void> const& f
30+
, typename call_traits<Translate>::param_type translate) const
31+
{
32+
try
33+
{
34+
return handler(f);
35+
}
36+
catch(exception_cref e)
37+
{
38+
translate(e);
39+
return true;
40+
}
41+
}
42+
};
43+
44+
}}} // namespace boost::python::detail
45+
46+
#endif // TRANSLATE_EXCEPTION_DWA2002810_HPP

include/boost/python/errors.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ struct BOOST_PYTHON_DECL argument_error : error_already_set {};
2020

2121
// Handles exceptions caught just before returning to Python code.
2222
// Returns true iff an exception was caught.
23-
BOOST_PYTHON_DECL bool handle_exception_impl(function0<void>);
23+
BOOST_PYTHON_DECL bool handle_exception_impl(function0<void> const&);
2424

2525
template <class T>
2626
bool handle_exception(T f)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright David Abrahams 2002. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef EXCEPTION_TRANSLATOR_DWA2002810_HPP
7+
# define EXCEPTION_TRANSLATOR_DWA2002810_HPP
8+
# include <boost/python/detail/translate_exception.hpp>
9+
# include <boost/python/detail/exception_handler.hpp>
10+
# include <boost/type.hpp>
11+
# include <boost/bind.hpp>
12+
13+
namespace boost { namespace python {
14+
15+
template <class ExceptionType, class Translate>
16+
void register_exception_translator(Translate const& translate, boost::type<ExceptionType>* = 0)
17+
{
18+
detail::register_exception_handler(
19+
bind<bool>(detail::translate_exception<ExceptionType,Translate>(), _1, _2, translate)
20+
);
21+
}
22+
23+
}} // namespace boost::python
24+
25+
#endif // EXCEPTION_TRANSLATOR_DWA2002810_HPP

src/errors.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@
1010

1111
#include <boost/python/errors.hpp>
1212
#include <boost/cast.hpp>
13+
#ifdef BOOST_PYTHON_V2
14+
# include <boost/python/detail/exception_handler.hpp>
15+
#endif
1316

1417
namespace boost { namespace python {
1518

1619
// IMPORTANT: this function may only be called from within a catch block!
17-
BOOST_PYTHON_DECL bool handle_exception_impl(function0<void> f)
20+
BOOST_PYTHON_DECL bool handle_exception_impl(function0<void> const& f)
1821
{
1922
try
2023
{
24+
#ifdef BOOST_PYTHON_V2
25+
if (detail::exception_handler::chain)
26+
return detail::exception_handler::chain->handle(f);
27+
#endif
2128
f();
2229
return false;
2330
}
@@ -68,6 +75,42 @@ namespace detail {
6875
// needed by void_adaptor (see void_adaptor.hpp)
6976
BOOST_PYTHON_DECL PyObject arbitrary_object = { 0 };
7077

78+
#ifdef BOOST_PYTHON_V2
79+
bool exception_handler::operator()(function0<void> const& f) const
80+
{
81+
if (m_next)
82+
{
83+
return m_next->handle(f);
84+
}
85+
else
86+
{
87+
f();
88+
return false;
89+
}
90+
}
91+
92+
exception_handler::exception_handler(handler_function const& impl)
93+
: m_impl(impl)
94+
, m_next(0)
95+
{
96+
if (chain != 0)
97+
tail->m_next = this;
98+
else
99+
chain = this;
100+
tail = this;
101+
}
102+
103+
exception_handler* exception_handler::chain;
104+
exception_handler* exception_handler::tail;
105+
106+
BOOST_PYTHON_DECL void register_exception_handler(handler_function const& f)
107+
{
108+
// the constructor links the new object into a handler chain, so
109+
// this object isn't actaully leaked (until, of course, the
110+
// interpreter exits).
111+
new exception_handler(f);
112+
}
113+
#endif
71114

72115
} // namespace boost::python::detail
73116

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ rule bpl-test ( name ? : files * )
5656

5757
bpl-test minimal ;
5858
bpl-test docstring ;
59+
bpl-test exception_translator ;
5960
bpl-test pearu1 : test_cltree.py cltree.cpp ;
6061
bpl-test try : newtest.py m1.cpp m2.cpp ;
6162
bpl-test builtin_converters : test_builtin_converters.py test_builtin_converters.cpp ;

test/exception_translator.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <boost/python/module.hpp>
2+
#include <boost/python/exception_translator.hpp>
3+
4+
struct error {};
5+
6+
void translate(error const& e)
7+
{
8+
PyErr_SetString(PyExc_RuntimeError, "!!!error!!!");
9+
}
10+
11+
void throw_error()
12+
{
13+
throw error();
14+
15+
}
16+
17+
BOOST_PYTHON_MODULE_INIT(exception_translator_ext)
18+
{
19+
using namespace boost::python;
20+
21+
register_exception_translator<error>(&translate);
22+
module("exception_translator_ext")
23+
.def("throw_error", throw_error);
24+
}
25+

test/exception_translator.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'''
2+
>>> from exception_translator_ext import *
3+
>>> try:
4+
... throw_error();
5+
... except RuntimeError, x:
6+
... print x
7+
... else:
8+
... print 'Expected a RuntimeError!'
9+
!!!error!!!
10+
'''
11+
def run(args = None):
12+
import sys
13+
import doctest
14+
15+
if args is not None:
16+
sys.argv = args
17+
return doctest.testmod(sys.modules.get(__name__))
18+
19+
if __name__ == '__main__':
20+
print "running..."
21+
import sys
22+
sys.exit(run()[0])

0 commit comments

Comments
 (0)