diff --git a/extclass.h b/extclass.h index f130121c..6f330528 100644 --- a/extclass.h +++ b/extclass.h @@ -132,11 +132,32 @@ template > class PyExtensionClassConverters { public: -#ifdef BOOST_MSVC - // Convert return values of type T to python objects. What happens if T is - // not copyable? Apparently there is no problem with g++ or MSVC unless this - // is actually used. With a conforming compiler we will have a problem. - friend PyObject* to_python(const T& x) + // Get an object which can be used to convert T to/from python. This is used + // as a kind of concept check by the global template + // + // from_python(PyObject*, py::Type) + // + // below this class, to prevent the confusing messages that would otherwise + // pop up. Now, if T hasn't been wrapped as an extension class, the user + // will see an error message about the lack of an eligible + // py_extension_class_converters() function. + friend PyExtensionClassConverters py_extension_class_converters(py::Type) + { + return PyExtensionClassConverters(); + } + + // This is a member function because in a conforming implementation, friend + // funcitons defined inline in the class body are all instantiated as soon + // as the enclosing class is instantiated. If T is not copyable, that causes + // a compiler error. Instead, we access this function through the global + // template + // + // from_python(PyObject*, py::Type) + // + // defined below this class. Since template functions are instantiated only + // on demand, errors will be avoided unless T is noncopyable and the user + // writes code which causes us to try to copy a T. + PyObject* to_python(const T& x) const { py::PyPtr result(create_instance()); result->add_implementation( @@ -144,11 +165,7 @@ class PyExtensionClassConverters new py::InstanceValueHolder(result.get(), x))); return result.release(); } -#else - friend py::Type py_holder_type(const T&) - { return py::Type(); } -#endif - + // Convert to T* friend T* from_python(PyObject* obj, py::Type) { @@ -251,25 +268,15 @@ class PyExtensionClassConverters { return ptr_to_python(x); } }; -#ifndef BOOST_MSVC -template -py::InstanceHolderBase* -py_copy_to_new_value_holder(py::ExtensionInstance* p, const T& x, py::Type) -{ - return new py::InstanceValueHolder(p, x); -} - +// Convert T to_python, instantiated on demand and only if there isn't a +// non-template overload for this function. This version is the one invoked when +// T is a wrapped class. See the first 2 functions declared in +// PyExtensionClassConverters above for more info. template PyObject* to_python(const T& x) { - py::PyPtr result( - PyExtensionClassConverters::create_instance()); - result->add_implementation( - std::auto_ptr( - py_copy_to_new_value_holder(result.get(), x, py_holder_type(x)))); - return result.release(); + return py_extension_class_converters(py::Type()).to_python(x); } -#endif #ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back from global namespace for this GCC bug namespace py { diff --git a/gen_extclass.py b/gen_extclass.py index bbce80a6..ebd2fc78 100644 --- a/gen_extclass.py +++ b/gen_extclass.py @@ -137,11 +137,32 @@ template > class PyExtensionClassConverters { public: -#ifdef BOOST_MSVC - // Convert return values of type T to python objects. What happens if T is - // not copyable? Apparently there is no problem with g++ or MSVC unless this - // is actually used. With a conforming compiler we will have a problem. - friend PyObject* to_python(const T& x) + // Get an object which can be used to convert T to/from python. This is used + // as a kind of concept check by the global template + // + // from_python(PyObject*, py::Type) + // + // below this class, to prevent the confusing messages that would otherwise + // pop up. Now, if T hasn't been wrapped as an extension class, the user + // will see an error message about the lack of an eligible + // py_extension_class_converters() function. + friend PyExtensionClassConverters py_extension_class_converters(py::Type) + { + return PyExtensionClassConverters(); + } + + // This is a member function because in a conforming implementation, friend + // funcitons defined inline in the class body are all instantiated as soon + // as the enclosing class is instantiated. If T is not copyable, that causes + // a compiler error. Instead, we access this function through the global + // template + // + // from_python(PyObject*, py::Type) + // + // defined below this class. Since template functions are instantiated only + // on demand, errors will be avoided unless T is noncopyable and the user + // writes code which causes us to try to copy a T. + PyObject* to_python(const T& x) const { py::PyPtr result(create_instance()); result->add_implementation( @@ -149,11 +170,7 @@ class PyExtensionClassConverters new py::InstanceValueHolder(result.get(), x))); return result.release(); } -#else - friend py::Type py_holder_type(const T&) - { return py::Type(); } -#endif - + // Convert to T* friend T* from_python(PyObject* obj, py::Type) { @@ -256,25 +273,15 @@ class PyExtensionClassConverters { return ptr_to_python(x); } }; -#ifndef BOOST_MSVC -template -py::InstanceHolderBase* -py_copy_to_new_value_holder(py::ExtensionInstance* p, const T& x, py::Type) -{ - return new py::InstanceValueHolder(p, x); -} - +// Convert T to_python, instantiated on demand and only if there isn't a +// non-template overload for this function. This version is the one invoked when +// T is a wrapped class. See the first 2 functions declared in +// PyExtensionClassConverters above for more info. template PyObject* to_python(const T& x) { - py::PyPtr result( - PyExtensionClassConverters::create_instance()); - result->add_implementation( - std::auto_ptr( - py_copy_to_new_value_holder(result.get(), x, py_holder_type(x)))); - return result.release(); + return py_extension_class_converters(py::Type()).to_python(x); } -#endif #ifdef PY_NO_INLINE_FRIENDS_IN_NAMESPACE // back from global namespace for this GCC bug namespace py {