diff --git a/include/boost/python/converter/implicit.hpp b/include/boost/python/converter/implicit.hpp index 95372810..23c2ece5 100644 --- a/include/boost/python/converter/implicit.hpp +++ b/include/boost/python/converter/implicit.hpp @@ -37,7 +37,7 @@ struct implicit registration->construct(obj, &intermediate_data.stage1); void* storage = ((rvalue_base_data*)data)->storage.bytes; - new (storage) Target(*(Source*)intermediate_data.storage.bytes); + new (storage) Target(*static_cast(intermediate_data.stage1.convertible)); // record successful construction data->convertible = storage; diff --git a/include/boost/python/converter/registry.hpp b/include/boost/python/converter/registry.hpp index 1c8dba24..72676605 100644 --- a/include/boost/python/converter/registry.hpp +++ b/include/boost/python/converter/registry.hpp @@ -37,6 +37,14 @@ namespace registry , undecorated_type_id_t ); + // Insert an rvalue from_python converter at the tail of the + // chain. Used for implicit conversions + BOOST_PYTHON_DECL void push_back( + void* (*convertible)(PyObject*) + , constructor_function + , undecorated_type_id_t + ); + BOOST_PYTHON_DECL PyTypeObject*& class_object(undecorated_type_id_t key); } diff --git a/include/boost/python/implicit.hpp b/include/boost/python/implicit.hpp index b13898e8..f7146e40 100644 --- a/include/boost/python/implicit.hpp +++ b/include/boost/python/implicit.hpp @@ -17,7 +17,7 @@ void implicitly_convertible(boost::type* = 0, boost::type* = 0) { typedef converter::implicit functions; - converter::registry::insert( + converter::registry::push_back( &functions::convertible , &functions::construct , converter::undecorated_type_id()); diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index f821201a..805902a5 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include namespace boost { namespace python { namespace converter { @@ -29,15 +31,38 @@ BOOST_PYTHON_DECL rvalue_stage1_data find( return data; } +namespace +{ + // Prevent looping in implicit conversions. This could/should be + // much more efficient, but will work for now. + typedef std::vector visited_t; + static visited_t visited; +} + BOOST_PYTHON_DECL rvalue_from_python_registration const* find_chain( PyObject* source , rvalue_from_python_registration const* chain) -{ - for (;chain != 0; chain = chain->next) +{ + visited_t::iterator p = std::lower_bound(visited.begin(), visited.end(), chain); + if (p != visited.end() && *p == chain) + return 0; + + visited.insert(p, chain); + try { - if (chain->convertible(source)) - break; + for (;chain != 0; chain = chain->next) + { + if (chain->convertible(source)) + break; + } } + catch(...) + { + visited.erase(p); + throw; + } + p = std::lower_bound(visited.begin(), visited.end(), chain); + visited.erase(p); return chain; } diff --git a/src/converter/registry.cpp b/src/converter/registry.cpp index 2d104b01..822812e3 100644 --- a/src/converter/registry.cpp +++ b/src/converter/registry.cpp @@ -109,6 +109,22 @@ namespace registry found->m_rvalue_from_python = registration; } + // Insert an rvalue from_python converter + void push_back(void* (*convertible)(PyObject*) + , constructor_function construct + , undecorated_type_id_t key) + { + rvalue_from_python_registration** found = &find(key)->m_rvalue_from_python; + while (*found != 0) + found = &(*found)->next; + + rvalue_from_python_registration *registration = new rvalue_from_python_registration; + registration->convertible = convertible; + registration->construct = construct; + registration->next = 0; + *found = registration; + } + PyTypeObject*& class_object(undecorated_type_id_t key) { return find(key)->m_class_object; diff --git a/test/implicit.cpp b/test/implicit.cpp index 17cbe7f7..2b017fb4 100644 --- a/test/implicit.cpp +++ b/test/implicit.cpp @@ -14,24 +14,35 @@ using namespace boost::python; typedef test_class<> X; +typedef test_class<1> Y; int x_value(X const& x) { return x.value(); } +X make_x(int n) { return X(n); } + BOOST_PYTHON_MODULE_INIT(implicit_ext) { implicitly_convertible(); module("implicit_ext") .def("x_value", x_value) + .def("make_x", make_x) .add( class_("X") .def_init(args()) .def("value", &X::value) .def("set", &X::set) ) + .add( + class_("Y") + .def_init(args()) + .def("value", &Y::value) + .def("set", &Y::set) + ) ; + implicitly_convertible(); } #include "module_tail.cpp" diff --git a/test/implicit.py b/test/implicit.py index 322467c0..e0d8c067 100644 --- a/test/implicit.py +++ b/test/implicit.py @@ -4,6 +4,12 @@ 42 >>> x_value(42) 42 +>>> x = make_x(X(42)) +>>> x.value() +42 +>>> try: make_x('fool') +... except TypeError: pass +... else: print 'no error' ''' def run(args = None): diff --git a/test/test_class.hpp b/test/test_class.hpp index 920a6b1f..414cd084 100644 --- a/test/test_class.hpp +++ b/test/test_class.hpp @@ -16,6 +16,7 @@ struct test_class void set(int x) { assert(magic == 7654321 + n); this->x = x; } int value() const { assert(magic == 7654321 + n); return x; } + operator int() const { return x; } static int count() { return counter; } private: void operator=(test_class const&);