From a9baa519f3e0d389a9c20cd443272e3d59ff2c81 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Thu, 18 Jul 2002 05:00:34 +0000 Subject: [PATCH] Extract implemented [SVN r14510] --- .../always_extract_object_manager.hpp | 35 --- .../boost/python/converter/from_python.hpp | 5 + .../converter/obj_mgr_arg_from_python.hpp | 4 +- .../boost/python/converter/object_manager.hpp | 14 +- ...r.hpp => pytype_object_manager_traits.hpp} | 18 +- .../python/converter/return_from_python.hpp | 2 +- include/boost/python/extract.hpp | 239 ++++++++++++++++++ include/boost/python/list.hpp | 6 +- include/boost/python/long.hpp | 6 +- include/boost/python/object_core.hpp | 28 +- src/converter/from_python.cpp | 98 ++++--- test/Jamfile | 2 + test/extract.cpp | 118 +++++++++ test/extract.py | 102 ++++++++ 14 files changed, 568 insertions(+), 109 deletions(-) delete mode 100644 include/boost/python/converter/always_extract_object_manager.hpp rename include/boost/python/converter/{pytype_extract_object_manager.hpp => pytype_object_manager_traits.hpp} (67%) create mode 100644 include/boost/python/extract.hpp create mode 100644 test/extract.cpp create mode 100644 test/extract.py diff --git a/include/boost/python/converter/always_extract_object_manager.hpp b/include/boost/python/converter/always_extract_object_manager.hpp deleted file mode 100644 index 02f18143..00000000 --- a/include/boost/python/converter/always_extract_object_manager.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// 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 ALWAYS_EXTRACT_OBJECT_MANAGER_DWA2002716_HPP -# define ALWAYS_EXTRACT_OBJECT_MANAGER_DWA2002716_HPP - -# include - -namespace boost { namespace python { namespace converter { - -// Derive specializations of extract_object_manager from this when the -// object manager is indiscriminate about the Python objects it manages -struct always_extract_object_manager -{ - BOOST_STATIC_CONSTANT(bool, is_specialized = true); - static inline bool check(PyObject* x); -}; - -// Provide a forward declaration as a convenience for clients, who all -// need it. -template struct extract_object_manager; - -// -// implementations -// -inline bool always_extract_object_manager::check(PyObject* x) -{ - return true; -} - -}}} // namespace boost::python::converter - -#endif // ALWAYS_EXTRACT_OBJECT_MANAGER_DWA2002716_HPP diff --git a/include/boost/python/converter/from_python.hpp b/include/boost/python/converter/from_python.hpp index 26373ce0..dabafcaa 100644 --- a/include/boost/python/converter/from_python.hpp +++ b/include/boost/python/converter/from_python.hpp @@ -24,6 +24,9 @@ BOOST_PYTHON_DECL rvalue_from_python_chain const* implicit_conversion_chain( BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1( PyObject* source, registration const&); +BOOST_PYTHON_DECL void* rvalue_from_python_stage2( + PyObject* source, rvalue_from_python_stage1_data&, registration const&); + BOOST_PYTHON_DECL void* rvalue_result_from_python( PyObject*, rvalue_from_python_stage1_data&); @@ -32,6 +35,8 @@ BOOST_PYTHON_DECL void* pointer_result_from_python(PyObject*, registration const BOOST_PYTHON_DECL void void_result_from_python(PyObject*); +BOOST_PYTHON_DECL void throw_no_pointer_from_python(PyObject*, registration const&); +BOOST_PYTHON_DECL void throw_no_reference_from_python(PyObject*, registration const&); }}} // namespace boost::python::converter diff --git a/include/boost/python/converter/obj_mgr_arg_from_python.hpp b/include/boost/python/converter/obj_mgr_arg_from_python.hpp index 77e1ab8e..ee0980a5 100644 --- a/include/boost/python/converter/obj_mgr_arg_from_python.hpp +++ b/include/boost/python/converter/obj_mgr_arg_from_python.hpp @@ -57,7 +57,7 @@ inline object_manager_value_arg_from_python::object_manager_value_arg_from_py template inline bool object_manager_value_arg_from_python::convertible() const { - return extract_object_manager::check(m_source); + return object_manager_traits::check(m_source); } template @@ -83,7 +83,7 @@ namespace detail template inline bool object_manager_ref_check(T const& x) { - return extract_object_manager::check((get_managed_object)(x)); + return object_manager_traits::check((get_managed_object)(x)); } } diff --git a/include/boost/python/converter/object_manager.hpp b/include/boost/python/converter/object_manager.hpp index ac0ceb02..b9d64286 100755 --- a/include/boost/python/converter/object_manager.hpp +++ b/include/boost/python/converter/object_manager.hpp @@ -39,13 +39,15 @@ namespace boost { namespace python { namespace converter { // // X::is_specialized == true // -// T(X::execute(p)) - constructs a T object from p, or throws a -// TypeError exception if p doesn't have an appropriate type. +// T(X::adopt(p)) - constructs a T object, stealing a reference to +// p, or throws a TypeError exception if p doesn't have an +// appropriate type. // -// X::check(p), convertible to bool. True iff T(X::execute(p)) will +// X::check(p), convertible to bool. True iff T(X::construct(p)) will // not throw. +// template -struct extract_object_manager +struct object_manager_traits { BOOST_STATIC_CONSTANT(bool, is_specialized = false); }; @@ -58,9 +60,9 @@ struct is_object_manager // handle the cases that would otherwise require partial specialization BOOST_STATIC_CONSTANT(bool, hdl = is_handle::value); BOOST_STATIC_CONSTANT(bool, borrowed = python::detail::is_borrowed_ptr::value); - BOOST_STATIC_CONSTANT(bool, extract_specialized = extract_object_manager::is_specialized); + BOOST_STATIC_CONSTANT(bool, traits_specialized = object_manager_traits::is_specialized); public: - BOOST_STATIC_CONSTANT(bool, value = (hdl | borrowed | extract_specialized)); + BOOST_STATIC_CONSTANT(bool, value = (hdl | borrowed | traits_specialized)); }; # ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION diff --git a/include/boost/python/converter/pytype_extract_object_manager.hpp b/include/boost/python/converter/pytype_object_manager_traits.hpp similarity index 67% rename from include/boost/python/converter/pytype_extract_object_manager.hpp rename to include/boost/python/converter/pytype_object_manager_traits.hpp index cd4cadc9..24893ea5 100644 --- a/include/boost/python/converter/pytype_extract_object_manager.hpp +++ b/include/boost/python/converter/pytype_object_manager_traits.hpp @@ -3,8 +3,8 @@ // 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 PYTYPE_EXTRACT_OBJECT_MANAGER_DWA2002716_HPP -# define PYTYPE_EXTRACT_OBJECT_MANAGER_DWA2002716_HPP +#ifndef PYTYPE_OBJECT_MANAGER_TRAITS_DWA2002716_HPP +# define PYTYPE_OBJECT_MANAGER_TRAITS_DWA2002716_HPP # include # include @@ -15,16 +15,16 @@ namespace boost { namespace python { namespace converter { // Provide a forward declaration as a convenience for clients, who all // need it. -template struct extract_object_manager; +template struct object_manager_traits; -// Derive specializations of extract_object_manager from this class +// Derive specializations of object_manager_traits from this class // when T is an object manager for a particular Python type hierarchy. // template -struct pytype_extract_object_manager +struct pytype_object_manager_traits { BOOST_STATIC_CONSTANT(bool, is_specialized = true); - static inline python::detail::new_reference execute(PyObject*); + static inline python::detail::new_reference adopt(PyObject*); static inline bool check(PyObject* x); }; @@ -32,17 +32,17 @@ struct pytype_extract_object_manager // implementations // template -inline python::detail::new_reference pytype_extract_object_manager::execute(PyObject* x) +inline python::detail::new_reference pytype_object_manager_traits::adopt(PyObject* x) { return pytype_result_from_python(pytype, x); } template -inline bool pytype_extract_object_manager::check(PyObject* x) +inline bool pytype_object_manager_traits::check(PyObject* x) { return ::PyObject_IsInstance(x, python::upcast(pytype)); } }}} // namespace boost::python::converter -#endif // PYTYPE_EXTRACT_OBJECT_MANAGER_DWA2002716_HPP +#endif // PYTYPE_OBJECT_MANAGER_TRAITS_DWA2002716_HPP diff --git a/include/boost/python/converter/return_from_python.hpp b/include/boost/python/converter/return_from_python.hpp index 1f2d74cf..490d4bf7 100755 --- a/include/boost/python/converter/return_from_python.hpp +++ b/include/boost/python/converter/return_from_python.hpp @@ -140,7 +140,7 @@ namespace detail inline T return_object_manager_from_python::operator()(PyObject* obj) const { return T( - extract_object_manager::execute(obj) + object_manager_traits::adopt(obj) ); } } diff --git a/include/boost/python/extract.hpp b/include/boost/python/extract.hpp new file mode 100644 index 00000000..18d8c5c2 --- /dev/null +++ b/include/boost/python/extract.hpp @@ -0,0 +1,239 @@ +// 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 EXTRACT_DWA200265_HPP +# define EXTRACT_DWA200265_HPP + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +namespace boost { namespace python { + +namespace converter +{ + template + struct extract_pointer + { + typedef Ptr result_type; + extract_pointer(PyObject*); + + bool check() const; + Ptr operator()() const; + + private: + PyObject* m_source; + void* m_result; + }; + + template + struct extract_reference + { + typedef Ref result_type; + extract_reference(PyObject*); + + bool check() const; + Ref operator()() const; + + private: + PyObject* m_source; + void* m_result; + }; + + template + struct extract_rvalue : private noncopyable + { + typedef typename call_traits::param_type result_type; + extract_rvalue(PyObject*); + + bool check() const; + result_type operator()() const; + private: + PyObject* m_source; + mutable rvalue_from_python_data m_data; + }; + + template + struct extract_object_manager + { + typedef T result_type; + extract_object_manager(PyObject*); + + bool check() const; + result_type operator()() const; + private: + PyObject* m_source; + }; + + template + struct select_extract + { + BOOST_STATIC_CONSTANT( + bool, obj_mgr = is_object_manager::value); + + BOOST_STATIC_CONSTANT( + bool, ptr = is_pointer::value); + + BOOST_STATIC_CONSTANT( + bool, ref = is_reference::value); + + typedef typename mpl::select_type< + obj_mgr + , extract_object_manager + , typename mpl::select_type< + ptr + , extract_pointer + , typename mpl::select_type< + ref + , extract_reference + , extract_rvalue + >::type + >::type + >::type type; + }; +} + +template +struct extract + : converter::select_extract::type +{ + private: + typedef typename converter::select_extract::type base; + typedef bool (extract::*bool_type)() const; + public: + typedef typename base::result_type result_type; + operator result_type() const + { + return (*this)(); + } + + extract(PyObject*); + extract(object const&); +}; + +// +// Implementations +// +template +inline extract::extract(PyObject* o) + : base(o) +{ +} + +template +inline extract::extract(object const& o) + : base(o.ptr()) +{ +} + +namespace converter +{ + template + inline extract_rvalue::extract_rvalue(PyObject* x) + : m_source(x) + , m_data( + (rvalue_from_python_stage1)(x, registered::converters) + ) + { + } + + template + inline bool + extract_rvalue::check() const + { + return m_data.stage1.convertible; + } + + template + inline typename extract_rvalue::result_type + extract_rvalue::operator()() const + { + return *(T*)( + // Only do the stage2 conversion once + m_data.stage1.convertible == m_data.storage.bytes + ? m_data.storage.bytes + : (rvalue_from_python_stage2)(m_source, m_data.stage1, registered::converters) + ); + } + + template + inline extract_reference::extract_reference(PyObject* obj) + : m_source(obj) + , m_result( + (get_lvalue_from_python)(obj, registered::converters) + ) + { + } + + template + inline bool extract_reference::check() const + { + return m_result != 0; + } + + template + inline Ref extract_reference::operator()() const + { + if (m_result == 0) + (throw_no_reference_from_python)(m_source, registered::converters); + + return python::detail::void_ptr_to_reference(m_result, (Ref(*)())0); + } + + template + inline extract_pointer::extract_pointer(PyObject* obj) + : m_source(obj) + , m_result( + obj == Py_None ? 0 : (get_lvalue_from_python)(obj, registered_pointee::converters) + ) + { + } + + template + inline bool extract_pointer::check() const + { + return m_source == Py_None || m_result != 0; + } + + template + inline Ptr extract_pointer::operator()() const + { + if (m_result == 0 && m_source != Py_None) + (throw_no_pointer_from_python)(m_source, registered_pointee::converters); + + return Ptr(m_result); + } + + template + inline extract_object_manager::extract_object_manager(PyObject* obj) + : m_source(obj) + { + } + + template + inline bool extract_object_manager::check() const + { + return object_manager_traits::check(m_source); + } + + template + inline T extract_object_manager::operator()() const + { + return T( + object_manager_traits::adopt(python::incref(m_source)) + ); + } +} + +}} // namespace boost::python::converter + +#endif // EXTRACT_DWA200265_HPP diff --git a/include/boost/python/list.hpp b/include/boost/python/list.hpp index 63ee469b..8cff1670 100644 --- a/include/boost/python/list.hpp +++ b/include/boost/python/list.hpp @@ -7,7 +7,7 @@ # define LIST_DWA2002627_HPP # include -# include +# include namespace boost { namespace python { @@ -106,8 +106,8 @@ class list : public object namespace converter { template <> - struct extract_object_manager - : pytype_extract_object_manager<&PyList_Type,list> + struct object_manager_traits + : pytype_object_manager_traits<&PyList_Type,list> { }; } diff --git a/include/boost/python/long.hpp b/include/boost/python/long.hpp index 0f1fcdf6..f5dd829e 100644 --- a/include/boost/python/long.hpp +++ b/include/boost/python/long.hpp @@ -7,7 +7,7 @@ # define LONG_DWA2002627_HPP # include -# include +# include namespace boost { namespace python { @@ -44,8 +44,8 @@ class long_ : public object namespace converter { template <> - struct extract_object_manager - : pytype_extract_object_manager<&PyLong_Type,long_> + struct object_manager_traits + : pytype_object_manager_traits<&PyLong_Type,long_> { }; } diff --git a/include/boost/python/object_core.hpp b/include/boost/python/object_core.hpp index 4ac0dcea..72e9e5ed 100755 --- a/include/boost/python/object_core.hpp +++ b/include/boost/python/object_core.hpp @@ -14,7 +14,6 @@ # include # include # include -# include # include # include @@ -274,19 +273,6 @@ namespace api } using api::object; -// -// Converter Specializations -// -namespace converter -{ - template <> - struct extract_object_manager - : always_extract_object_manager - { - static python::detail::borrowed_reference execute(PyObject* x); - }; -} - // // implementation // @@ -343,11 +329,17 @@ inline PyObject* api::object_base::ptr() const // namespace converter { - inline python::detail::borrowed_reference - extract_object_manager::execute(PyObject* x) + template <> + struct object_manager_traits { - return python::detail::borrowed_reference(python::incref(x)); - } + BOOST_STATIC_CONSTANT(bool, is_specialized = true); + static bool check(PyObject*) { return true; } + + static python::detail::new_non_null_reference adopt(PyObject* x) + { + return python::detail::new_non_null_reference(x); + } + }; inline PyObject* get_managed_object(object const& x) { diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index bac7f2f7..7565af38 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -77,14 +77,19 @@ BOOST_PYTHON_DECL void* rvalue_result_from_python( // Look for an eligible converter data = rvalue_from_python_stage1(src, converters); - + return rvalue_from_python_stage2(src, data, converters); +} + +BOOST_PYTHON_DECL void* rvalue_from_python_stage2( + PyObject* source, rvalue_from_python_stage1_data& data, registration const& converters) +{ if (!data.convertible) { handle<> msg( ::PyString_FromFormat( "No registered converter was able to produce a C++ rvalue of type %s from this Python object of type %s" , converters.target_type.name() - , src->ob_type->tp_name + , source->ob_type->tp_name )); PyErr_SetObject(PyExc_TypeError, msg.get()); @@ -94,7 +99,7 @@ BOOST_PYTHON_DECL void* rvalue_result_from_python( // If a construct function was registered (i.e. we found an // rvalue conversion), call it now. if (data.construct != 0) - data.construct(src, &data); + data.construct(source, &data); // Return the address of the resulting C++ object return data.convertible; @@ -165,37 +170,66 @@ BOOST_PYTHON_DECL rvalue_from_python_chain const* implicit_conversion_chain( return chain; } +namespace +{ + void throw_no_lvalue_from_python(PyObject* source, registration const& converters, char const* ref_type) + { + handle<> msg( + ::PyString_FromFormat( + "No registered converter was able to extract a C++ %s to type %s" + " from this Python object of type %s" + , ref_type + , converters.target_type.name() + , source->ob_type->tp_name + )); + + PyErr_SetObject(PyExc_TypeError, msg.get()); + + throw_error_already_set(); + } + + void* lvalue_result_from_python( + PyObject* source + , registration const& converters + , char const* ref_type) + { + handle<> holder(source); + if (source->ob_refcnt <= 2) + { + handle<> msg( + ::PyString_FromFormat( + "Attempt to return dangling %s to object of type: %s" + , ref_type + , converters.target_type.name())); + + PyErr_SetObject(PyExc_ReferenceError, msg.get()); + + throw_error_already_set(); + } + + void* result = get_lvalue_from_python(source, converters); + if (!result) + (throw_no_lvalue_from_python)(source, converters, ref_type); + return result; + } + +} + +BOOST_PYTHON_DECL void throw_no_pointer_from_python(PyObject* source, registration const& converters) +{ + (throw_no_lvalue_from_python)(source, converters, "pointer"); +} + +BOOST_PYTHON_DECL void throw_no_reference_from_python(PyObject* source, registration const& converters) +{ + (throw_no_lvalue_from_python)(source, converters, "reference"); +} + BOOST_PYTHON_DECL void* reference_result_from_python( PyObject* source , registration const& converters) { - handle<> holder(source); - if (source->ob_refcnt <= 2) - { - handle<> msg( - ::PyString_FromFormat( - "Attempt to return dangling pointer/reference to object of type: %s" - , converters.target_type.name())); - - PyErr_SetObject(PyExc_ReferenceError, msg.get()); - - throw_error_already_set(); - } - void* result = get_lvalue_from_python(source, converters); - if (!result) - { - handle<> msg( - ::PyString_FromFormat( - "No registered converter was able to extract a a C++ lvalue of type %s from this Python object of type %s" - , converters.target_type.name() - , source->ob_type->tp_name - )); - - PyErr_SetObject(PyExc_TypeError, msg.get()); - - throw_error_already_set(); - } - return result; + return (lvalue_result_from_python)(source, converters, "reference"); } BOOST_PYTHON_DECL void* pointer_result_from_python( @@ -207,7 +241,7 @@ BOOST_PYTHON_DECL void* pointer_result_from_python( Py_DECREF(source); return 0; } - return reference_result_from_python(source, converters); + return (lvalue_result_from_python)(source, converters, "pointer"); } BOOST_PYTHON_DECL void throw_no_class_registered() @@ -231,7 +265,7 @@ pytype_result_from_python(PyTypeObject* type_, PyObject* source) handle<> keeper(source); handle<> msg( ::PyString_FromFormat( - "Expecting a Python %s return type, but got an object of type %s instead" + "Expecting an object of type %s; got an object of type %s instead" , type_->tp_name , source->ob_type->tp_name )); diff --git a/test/Jamfile b/test/Jamfile index db78d895..1a73da53 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -75,6 +75,8 @@ bpl-test bienstman3 ; bpl-test multi_arg_constructor ; bpl-test iterator : iterator.py iterator.cpp input_iterator.cpp ; +bpl-test extract ; + if $(TEST_BIENSTMAN_NON_BUGS) { bpl-test bienstman4 ; diff --git a/test/extract.cpp b/test/extract.cpp new file mode 100644 index 00000000..0792e7c6 --- /dev/null +++ b/test/extract.cpp @@ -0,0 +1,118 @@ +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "test_class.hpp" +#include +#include + +using namespace boost::python; + +typedef test_class<> X; + +bool extract_bool(object x) { return extract(x); } + +boost::python::list extract_list(object x) +{ + extract get_list((x)); + + // Make sure we always have the right idea about whether it's a list + bool is_list_1 = get_list.check(); + bool is_list_2 = PyObject_IsInstance(x.ptr(), (PyObject*)&PyList_Type); + assert(is_list_1 == is_list_2); + + return get_list(); +} + +char const* extract_cstring(object x) { return extract(x); } +std::string extract_string(object x) +{ + std::string s = extract(x); + return s; +} + +std::string const& extract_string_cref(object x) +{ + std::string const& s = extract(x); + return s; +} + +X extract_X(object x) +{ + return extract(x); +} + +X* extract_X_ptr(object x) { return extract(x); } + +X& extract_X_ref(object x) +{ + extract get_x(x); + return get_x; +} + +int double_X(object n) +{ + extract x(n); + return x().value() + x().value(); +} + +bool check_bool(object x) { return extract(x).check(); } +bool check_list(object x) { return extract(x).check(); } +bool check_cstring(object x) { return extract(x).check(); } +bool check_string(object x) { return extract(x).check(); } +bool check_string_cref(object x) { return extract(x).check(); } +bool check_X(object x) { return extract(x).check(); } +bool check_X_ptr(object x) { return extract(x).check(); } +bool check_X_ref(object x) { return extract(x).check(); } + +std::string x_rep(X const& x) +{ + return "X(" + boost::lexical_cast(x.value()) + ")"; +} + +BOOST_PYTHON_MODULE_INIT(extract_ext) +{ + implicitly_convertible(); + + module("extract_ext") + .def("extract_bool", extract_bool) + .def("extract_list", extract_list) + .def("extract_cstring", extract_cstring) + .def("extract_string", extract_string) + .def("extract_string_cref", extract_string_cref, return_value_policy()) + .def("extract_X", extract_X) + .def("extract_X_ptr", extract_X_ptr, return_value_policy()) + .def("extract_X_ref", extract_X_ref, return_value_policy()) + + .def("check_bool", check_bool) + .def("check_list", check_list) + .def("check_cstring", check_cstring) + .def("check_string", check_string) + .def("check_string_cref", check_string_cref) + .def("check_X", check_X) + .def("check_X_ptr", check_X_ptr) + .def("check_X_ref", check_X_ref) + + .def("double_X", double_X) + + .def("count_Xs", &X::count) + + .add(class_("X") + .def_init(args()) + .def( "__repr__", x_rep)) + ; +} + + +#include "module_tail.cpp" + diff --git a/test/extract.py b/test/extract.py new file mode 100644 index 00000000..999dc7b3 --- /dev/null +++ b/test/extract.py @@ -0,0 +1,102 @@ +''' + >>> from extract_ext import * + +Just about anything has a truth value in Python + + >>> assert check_bool(None) + >>> extract_bool(None) + 0 + + >>> assert check_bool(2) + >>> extract_bool(2) + 1 + + >>> assert check_bool('') + +Check that object manager types work properly. These are a different +case because they wrap Python objects instead of being wrapped by them. + + >>> assert not check_list(2) + >>> try: x = extract_list(2) + ... except TypeError, x: + ... if str(x) != 'Expecting an object of type list; got an object of type int instead': + ... print x + ... else: + ... print 'expected an exception, got', x, 'instead' + +Can't extract a list from a tuple. Use list(x) to convert a sequence +to a list: + + >>> assert not check_list((1, 2, 3)) + >>> assert check_list([1, 2, 3]) + >>> extract_list([1, 2, 3]) + [1, 2, 3] + +Can get a char const* from a Python string: + + >>> assert check_cstring('hello') + >>> extract_cstring('hello') + 'hello' + +Can't get a char const* from a Python int: + + >>> assert not check_cstring(1) + >>> try: x = extract_cstring(1) + ... except TypeError: pass + ... else: + ... print 'expected an exception, got', x, 'instead' + +Extract an std::string (class) rvalue from a native Python type + + >>> assert check_string('hello') + >>> extract_string('hello') + 'hello' + +Constant references are not treated as rvalues for the purposes of +extract: + + >>> assert not check_string_cref('hello') + +We can extract lvalues where appropriate: + + >>> x = X(42) + >>> check_X(x) + 1 + >>> extract_X(x) + X(42) + + >>> check_X_ptr(x) + 1 + >>> extract_X_ptr(x) + X(42) + >>> extract_X_ref(x) + X(42) + +Demonstrate that double-extraction of an rvalue works, and all created +copies of the object are destroyed: + + >>> n = count_Xs() + >>> double_X(333) + 666 + >>> count_Xs() - n + 0 + +General check for cleanliness: + + >>> del x + >>> count_Xs() + 0 +''' + +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])