diff --git a/include/boost/python/converter/arg_to_python.hpp b/include/boost/python/converter/arg_to_python.hpp index deab7964..192639ed 100755 --- a/include/boost/python/converter/arg_to_python.hpp +++ b/include/boost/python/converter/arg_to_python.hpp @@ -29,8 +29,6 @@ template struct is_object_manager; namespace detail { - BOOST_PYTHON_DECL void throw_no_class_registered(); - template struct function_arg_to_python : handle<> { @@ -209,8 +207,6 @@ namespace detail inline PyObject* reference_arg_to_python::get_object(T& x) { to_python_indirect convert; - if (!convert.convertible()) - throw_no_class_registered(); return convert(x); } @@ -231,9 +227,7 @@ namespace detail inline PyObject* pointer_shallow_arg_to_python::get_object(Ptr x) { to_python_indirect convert; - if (!convert.convertible()) - throw_no_class_registered(); - return x ? convert(x) : python::detail::none(); + return convert(x); } } diff --git a/include/boost/python/converter/builtin_converters.hpp b/include/boost/python/converter/builtin_converters.hpp index 38b22cb4..c46e7e8f 100644 --- a/include/boost/python/converter/builtin_converters.hpp +++ b/include/boost/python/converter/builtin_converters.hpp @@ -37,8 +37,6 @@ namespace detail // a converter. struct builtin_to_python { - static bool convertible() { return true; } - // This information helps make_getter() decide whether to try to // return an internal reference or not. I don't like it much, // but it will have to serve for now. diff --git a/include/boost/python/converter/registrations.hpp b/include/boost/python/converter/registrations.hpp index 70c7df68..e5338f67 100644 --- a/include/boost/python/converter/registrations.hpp +++ b/include/boost/python/converter/registrations.hpp @@ -35,6 +35,10 @@ struct BOOST_PYTHON_DECL registration // Convert the appropriately-typed data to Python PyObject* to_python(void const volatile*) const; + // Return the class object, or raise an appropriate Python + // exception if no class has been registered. + PyTypeObject* get_class_object() const; + public: // data members. So sue me. const python::type_info target_type; @@ -45,7 +49,7 @@ struct BOOST_PYTHON_DECL registration rvalue_from_python_chain* rvalue_chain; // The class object associated with this type - PyTypeObject* class_object; + PyTypeObject* m_class_object; // The unique to_python converter for the associated C++ type. to_python_function_t m_to_python; @@ -64,7 +68,7 @@ inline registration::registration(type_info target_type) , lvalue_chain(0) , rvalue_chain(0) , m_to_python(0) - , class_object(0) + , m_class_object(0) {} inline bool operator<(registration const& lhs, registration const& rhs) diff --git a/include/boost/python/data_members.hpp b/include/boost/python/data_members.hpp index 1315c37f..a8ff3871 100644 --- a/include/boost/python/data_members.hpp +++ b/include/boost/python/data_members.hpp @@ -36,7 +36,6 @@ namespace detail typedef typename Policies::result_converter result_converter; typedef typename boost::add_reference::type source; typename mpl::apply1::type cr; - if (!cr.convertible()) return 0; if (!policies.precall(args_)) return 0; diff --git a/include/boost/python/enum.hpp b/include/boost/python/enum.hpp index 072a0626..1070c06b 100644 --- a/include/boost/python/enum.hpp +++ b/include/boost/python/enum.hpp @@ -17,12 +17,15 @@ struct enum_ : public objects::enum_base { typedef objects::enum_base base; + // Declare a new enumeration type in the current scope() enum_(char const* name); + + // Add a new enumeration value with the given name and value. inline enum_& value(char const* name, T); private: static PyObject* to_python(void const* x); - static void* convertible(PyObject* obj); + static void* convertible_from_python(PyObject* obj); static void construct(PyObject* obj, converter::rvalue_from_python_stage1_data* data); }; @@ -31,32 +34,42 @@ inline enum_::enum_(char const* name) : base( name , &enum_::to_python - , &enum_::convertible + , &enum_::convertible_from_python , &enum_::construct , type_id()) { } -// This is the conversion function that gets registered for converting +// This is the conversion function that gets registered for converting +// these enums to Python. template PyObject* enum_::to_python(void const* x) { return base::to_python( - converter::registered::converters.class_object + converter::registered::converters.m_class_object , static_cast(*(T const*)x)); } +// +// The following two static functions serve as the elements of an +// rvalue from_python converter for the enumeration type. +// + +// This checks that a given Python object can be converted to the +// enumeration type. template -void* enum_::convertible(PyObject* obj) +void* enum_::convertible_from_python(PyObject* obj) { return PyObject_IsInstance( obj , upcast( - converter::registered::converters.class_object)) + converter::registered::converters.m_class_object)) ? obj : 0; } - + +// Constructs an instance of the enumeration type in the from_python +// data. template void enum_::construct(PyObject* obj, converter::rvalue_from_python_stage1_data* data) { diff --git a/include/boost/python/object/iterator.hpp b/include/boost/python/object/iterator.hpp index 65c206d7..c97c3994 100644 --- a/include/boost/python/object/iterator.hpp +++ b/include/boost/python/object/iterator.hpp @@ -87,7 +87,6 @@ namespace detail { typedef typename Policies::result_converter result_converter; typename mpl::apply1::type cr; - if (!cr.convertible()) return 0; return cr(x); } @@ -96,7 +95,6 @@ namespace detail { typedef typename Policies::result_converter result_converter; typename mpl::apply1::type cr; - if (!cr.convertible()) return 0; return cr(x); } @@ -150,11 +148,6 @@ namespace detail to_python_value > cr; - // This check is probably redundant, since we ensure the - // type is registered above. - if (!cr.convertible()) - return 0; - // Extract x from the first argument PyObject* arg0 = PyTuple_GET_ITEM(args_, 0); arg_from_python c0(arg0); diff --git a/include/boost/python/object/make_instance.hpp b/include/boost/python/object/make_instance.hpp index d5e153b9..ecac7493 100644 --- a/include/boost/python/object/make_instance.hpp +++ b/include/boost/python/object/make_instance.hpp @@ -20,7 +20,8 @@ struct make_instance static PyObject* execute(Arg& x) { BOOST_STATIC_ASSERT(is_class::value); - PyTypeObject* type = converter::registered::converters.class_object; + + PyTypeObject* type = converter::registered::converters.get_class_object(); PyObject* raw_result = type->tp_alloc( type, objects::additional_instance_size::value); diff --git a/include/boost/python/to_python_indirect.hpp b/include/boost/python/to_python_indirect.hpp index 23ff8594..78e95098 100644 --- a/include/boost/python/to_python_indirect.hpp +++ b/include/boost/python/to_python_indirect.hpp @@ -21,7 +21,6 @@ namespace boost { namespace python { template struct to_python_indirect { - static bool convertible(); PyObject* operator()(T ptr) const; private: static PyTypeObject* type(); @@ -90,16 +89,11 @@ namespace detail } } -template -inline bool to_python_indirect::convertible() -{ - BOOST_STATIC_ASSERT(is_pointer::value || is_reference::value); - return type() != 0; -} - template inline PyObject* to_python_indirect::operator()(T x) const { + BOOST_STATIC_ASSERT(is_pointer::value || is_reference::value); + PyObject* const null_result = detail::null_pointer_to_none(x, 1L); if (null_result != 0) return null_result; diff --git a/include/boost/python/to_python_value.hpp b/include/boost/python/to_python_value.hpp index de5122a2..17f6b174 100644 --- a/include/boost/python/to_python_value.hpp +++ b/include/boost/python/to_python_value.hpp @@ -27,7 +27,6 @@ namespace detail typename add_const::type >::type argument_type; - static bool convertible(); PyObject* operator()(argument_type) const; // This information helps make_getter() decide whether to try to @@ -44,7 +43,6 @@ namespace detail typename add_const::type >::type argument_type; - static bool convertible(); PyObject* operator()(argument_type) const; // This information helps make_getter() decide whether to try to @@ -73,24 +71,12 @@ struct to_python_value // namespace detail { - template - inline bool registry_to_python_value::convertible() - { - return converter::registered::converters.to_python != 0; - } - template inline PyObject* registry_to_python_value::operator()(argument_type x) const { return converter::registered::converters.to_python(&x); } - template - inline bool object_manager_to_python_value::convertible() - { - return true; - } - template inline PyObject* object_manager_to_python_value::operator()(argument_type x) const { diff --git a/src/converter/arg_to_python_base.cpp b/src/converter/arg_to_python_base.cpp index 6b143e64..1101f591 100644 --- a/src/converter/arg_to_python_base.cpp +++ b/src/converter/arg_to_python_base.cpp @@ -43,14 +43,6 @@ namespace detail (converters.to_python(source)) { } - - BOOST_PYTHON_DECL void throw_no_class_registered() - { - PyErr_SetString( - PyExc_TypeError - , const_cast("class not registered for to_python type")); - throw_error_already_set(); - } } }}} // namespace boost::python::converter diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index b6ce54ff..49b362e0 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -10,6 +10,8 @@ #include #include #include + +#include #include #include @@ -244,14 +246,6 @@ BOOST_PYTHON_DECL void* pointer_result_from_python( return (lvalue_result_from_python)(source, converters, "pointer"); } -BOOST_PYTHON_DECL void throw_no_class_registered() -{ - PyErr_SetString( - PyExc_TypeError - , const_cast("class not registered for to_python type")); - throw_error_already_set(); -} - BOOST_PYTHON_DECL void void_result_from_python(PyObject* o) { Py_DECREF(expect_non_null(o)); @@ -264,14 +258,12 @@ pytype_check(PyTypeObject* type_, PyObject* source) { if (!PyObject_IsInstance(source, python::upcast(type_))) { - handle<> keeper(source); - handle<> msg( - ::PyString_FromFormat( - "Expecting an object of type %s; got an object of type %s instead" - , type_->tp_name - , source->ob_type->tp_name - )); - PyErr_SetObject(PyExc_TypeError, msg.get()); + ::PyErr_Format( + PyExc_TypeError + , "Expecting an object of type %s; got an object of type %s instead" + , type_->tp_name + , source->ob_type->tp_name + ); throw_error_already_set(); } return source; diff --git a/src/converter/registry.cpp b/src/converter/registry.cpp index 87851eed..a3d7913d 100644 --- a/src/converter/registry.cpp +++ b/src/converter/registry.cpp @@ -6,6 +6,9 @@ #include #include #include + +#include + #include #include @@ -15,6 +18,23 @@ namespace boost { namespace python { namespace converter { +PyTypeObject* registration::get_class_object() const +{ + if (this->m_class_object == 0) + { + std::string name = lexical_cast(this->target_type); + ::PyErr_Format( + PyExc_TypeError + , const_cast("No Python class registered for C++ class %s") + , name.c_str()); + + throw_error_already_set(); + } + + return this->m_class_object; +} + + namespace // { typedef registration entry; diff --git a/src/object/class.cpp b/src/object/class.cpp index ad659cd5..3c261026 100644 --- a/src/object/class.cpp +++ b/src/object/class.cpp @@ -298,7 +298,7 @@ namespace objects converter::registration const* p = converter::registry::query(id); return type_handle( python::borrowed( - python::allow_null(p ? p->class_object : 0)) + python::allow_null(p ? p->m_class_object : 0)) ); } @@ -371,7 +371,7 @@ namespace objects converter::registry::lookup(types[0])); // Class object is leaked, for now - converters.class_object = (PyTypeObject*)incref(this->ptr()); + converters.m_class_object = (PyTypeObject*)incref(this->ptr()); } void class_base::set_instance_size(std::size_t instance_size) diff --git a/src/object/enum.cpp b/src/object/enum.cpp index b09a70a8..d151a486 100644 --- a/src/object/enum.cpp +++ b/src/object/enum.cpp @@ -169,7 +169,7 @@ enum_base::enum_base( = const_cast( converter::registry::lookup(id)); - converters.class_object = downcast(this->ptr()); + converters.m_class_object = downcast(this->ptr()); converter::registry::insert(to_python, id); converter::registry::insert(convertible, construct, id); } diff --git a/test/m1.cpp b/test/m1.cpp index 463b51ae..d9202b8b 100644 --- a/test/m1.cpp +++ b/test/m1.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include // Declare some straightforward extension types @@ -202,6 +203,8 @@ boost::shared_ptr d_factory() { return boost::shared_ptr(new D); } struct Unregistered {}; Unregistered make_unregistered(int) { return Unregistered(); } +Unregistered* make_unregistered2(int) { return new Unregistered; } + BOOST_PYTHON_MODULE(m1) { using namespace boost::python; @@ -226,6 +229,7 @@ BOOST_PYTHON_MODULE(m1) def("new_simple", new_simple); def("make_unregistered", make_unregistered); + def("make_unregistered2", make_unregistered2, return_value_policy()); // Expose f() in all its variations def("f", f); diff --git a/test/newtest.py b/test/newtest.py index 5162486a..da877151 100644 --- a/test/newtest.py +++ b/test/newtest.py @@ -6,13 +6,17 @@ Prove that we get an appropriate error from trying to return a type for which we have no registered to_python converter ->>> try: -... make_unregistered(1) -... except TypeError, x: -... if not str(x).startswith('No to_python (by-value) converter found for C++ type'): -... print str(x) -... else: -... print 'expected a TypeError' +>>> def check_unregistered(f, msgprefix): +... try: +... f(1) +... except TypeError, x: +... if not str(x).startswith(msgprefix): +... print str(x) +... else: +... print 'expected a TypeError' +... +>>> check_unregistered(make_unregistered, 'No to_python (by-value) converter found for C++ type') +>>> check_unregistered(make_unregistered2, 'No Python class registered for C++ class') >>> n = new_noddy() >>> s = new_simple()