From 22e82ae30fcba2d29245806a9e05f7ada476feeb Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sun, 12 Feb 2006 16:09:08 +0000 Subject: [PATCH] void pointer conversion support, from Niall Douglas, then heavily edited by DWA. Merged from python-voidptr [SVN r32857] --- doc/news.html | 6 + doc/v2/faq.html | 25 ---- include/boost/python/converter/registered.hpp | 24 +++- .../boost/python/opaque_pointer_converter.hpp | 132 +++++++++--------- .../boost/python/return_opaque_pointer.hpp | 45 +++--- include/boost/python/type_id.hpp | 17 +++ test/Jamfile | 1 + test/Jamfile.v2 | 1 + test/voidptr.py | 54 +++++++ 9 files changed, 181 insertions(+), 124 deletions(-) create mode 100644 test/voidptr.py diff --git a/doc/news.html b/doc/news.html index c3a71182..fbbf6080 100644 --- a/doc/news.html +++ b/doc/news.html @@ -39,6 +39,12 @@
  • New docstring_options.hpp header to control the content of docstrings. + +
  • Support for converting void* to/from python, + with opaque_pointer_converter + as the return value policy. Thanks to Niall Douglas for the + initial patch. diff --git a/doc/v2/faq.html b/doc/v2/faq.html index 690396c7..4d108b8d 100644 --- a/doc/v2/faq.html +++ b/doc/v2/faq.html @@ -67,8 +67,6 @@ >error C2064: term does not evaluate to a function taking 2 arguments -
    How do I handle void * conversion?
    -
    How can I automatically convert my custom string type to and from a Python string?
    @@ -693,29 +691,6 @@ void Export_FXThread() .def("setAutoDelete", (bool (FXThread::*)(bool)) &FXThread::setAutoDelete)

    (The bug has been reported to Microsoft.)

    -
    -

    How do I handle void * conversion?

    - Niall Douglas provides these notes:

    - For several reasons Boost.Python does not support void * as - an argument or as a return value. However, it is possible to wrap - functions with void * arguments or return values using - thin wrappers and the opaque pointer facility. E.g.: -

    // Declare the following in each translation unit
    -struct void_ {};
    -BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(void_);
    -
    -void *foo(int par1, void *par2);
    -
    -void_ *foo_wrapper(int par1, void_ *par2)
    -{
    -    return (void_ *) foo(par1, par2);
    -}
    -...
    -BOOST_PYTHON_MODULE(bar)
    -{
    -    def("foo", &foo_wrapper);
    -}
    -

    How can I automatically convert my custom string type to and from a Python string?

    diff --git a/include/boost/python/converter/registered.hpp b/include/boost/python/converter/registered.hpp index 28413c83..2404cb0f 100644 --- a/include/boost/python/converter/registered.hpp +++ b/include/boost/python/converter/registered.hpp @@ -9,7 +9,10 @@ # include # include # include +# include # include +# include +# include namespace boost { @@ -77,15 +80,30 @@ namespace detail } template - registration const& - registry_lookup(T&(*)()) + inline registration const& + registry_lookup2(T&(*)()) { detail::register_shared_ptr1((T*)0); return registry::lookup(type_id()); } template - registration const& registered_base::converters = detail::registry_lookup((T(*)())0); + inline registration const& + registry_lookup1(type) + { + return registry_lookup2((T(*)())0); + } + + inline registration const& + registry_lookup1(type) + { + detail::register_shared_ptr1((void*)0); + return registry::lookup(type_id()); + } + + template + registration const& registered_base::converters = detail::registry_lookup1(type()); + } }}} // namespace boost::python::converter diff --git a/include/boost/python/opaque_pointer_converter.hpp b/include/boost/python/opaque_pointer_converter.hpp index b01b3c13..e54561c7 100644 --- a/include/boost/python/opaque_pointer_converter.hpp +++ b/include/boost/python/opaque_pointer_converter.hpp @@ -13,15 +13,22 @@ # include # include # include +# include +# include + # include # include +# include -// opaque_pointer_converter -- +# include + +# include +# include +# include + +// opaque -- // -// usage: opaque_pointer_converter("name") -// -// registers to- and from- python conversions for a type Pointer, -// and a corresponding Python type called "name". +// registers to- and from- python conversions for a type Pointee. // // Note: // In addition you need to define specializations for type_id @@ -31,83 +38,65 @@ // For an example see libs/python/test/opaque.cpp // namespace boost { namespace python { - namespace detail { - template - struct opaque_pointer_converter_requires_a_pointer_type -# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__) - {} -# endif - ; - } -template -struct opaque_pointer_converter - : to_python_converter< - Pointer, opaque_pointer_converter > +template +struct opaque { - BOOST_STATIC_CONSTANT( - bool, ok = is_pointer::value); - - typedef typename mpl::if_c< - ok - , Pointer - , detail::opaque_pointer_converter_requires_a_pointer_type - >::type ptr_type; - -private: - struct instance; - -public: - explicit opaque_pointer_converter(char const* name) + opaque() { - type_object.tp_name = const_cast (name); - - lvalue_from_pytype< - opaque_pointer_converter, - &opaque_pointer_converter::type_object - >(); + type_object.tp_name = const_cast(type_id().name()); + converter::registry::insert(&extract, type_id()); + converter::registry::insert(&wrap, type_id()); + } + + static opaque instance; +private: + + static void* extract(PyObject* op) + { + return PyObject_TypeCheck(op, &type_object) + ? static_cast(implicit_cast(op))->x + : 0 + ; } - static PyObject* convert(ptr_type x) + static PyObject* wrap(void const* px) { - PyObject *result = 0; + Pointee* x = *static_cast(px); - if (x != 0) { - instance *o = PyObject_New (instance, &type_object); + if (x == 0) + return detail::none(); - o->x = x; - result = &o->base_; - } else { - result = detail::none(); + if ( python_instance *o = PyObject_New(python_instance, &type_object) ) + { + o->x = x; + return static_cast(implicit_cast(o)); + } + else + { + throw error_already_set(); } - - return (result); } - static typename ::boost::remove_pointer::type& - execute(instance &p_) + struct python_instance { - return *p_.x; - } - -private: - static PyTypeObject type_object; - - // This is a POD so we can use PyObject_Del on it, for example. - struct instance - { - PyObject base_; - ptr_type x; + PyObject_HEAD + Pointee* x; }; + + static PyTypeObject type_object; }; -template -PyTypeObject opaque_pointer_converter::type_object = +template +opaque opaque::instance; + +template +PyTypeObject opaque::type_object = { - PyObject_HEAD_INIT(NULL) + PyObject_HEAD_INIT(0) 0, 0, - sizeof(typename opaque_pointer_converter::instance), + sizeof( BOOST_DEDUCED_TYPENAME opaque::python_instance ), 0, ::boost::python::detail::dealloc, 0, /* tp_print */ @@ -155,11 +144,14 @@ PyTypeObject opaque_pointer_converter::type_object = #endif }; }} // namespace boost::python -# ifdef BOOST_MSVC -// MSC works without this workaround, but needs another one ... -# define BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(Pointee) \ - BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION(Pointee) + +# if BOOST_WORKAROUND(BOOST_MSVC, <= 1300) + +# define BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(Pointee) + # else + +// If you change the below, don't forget to alter the end of type_id.hpp # define BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(Pointee) \ namespace boost { namespace python { \ template<> \ @@ -173,6 +165,8 @@ PyTypeObject opaque_pointer_converter::type_object = { \ return type_info (typeid (Pointee *)); \ } \ -}} + }} + # endif + # endif // OPAQUE_POINTER_CONVERTER_HPP_ diff --git a/include/boost/python/return_opaque_pointer.hpp b/include/boost/python/return_opaque_pointer.hpp index daf9675b..cf544d80 100644 --- a/include/boost/python/return_opaque_pointer.hpp +++ b/include/boost/python/return_opaque_pointer.hpp @@ -10,47 +10,38 @@ # include # include -# include -# include +# include +# include +# include +# include namespace boost { namespace python { namespace detail { - template - struct opaque_conversion_holder + template + static void opaque_pointee(Pointee const volatile*) { - inline PyObject *operator()(Pointer p) const - { - static opaque_pointer_converter converter ( - typeid (Pointer).name()); - - return converter.convert(p); - } - }; - - template - struct return_opaque_pointer_requires_a_pointer_type -# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__) - {} -# endif - ; + force_instantiate(opaque::instance); + } } - + struct return_opaque_pointer { template struct apply { - BOOST_STATIC_CONSTANT( - bool, ok = is_pointer::value); + BOOST_MPL_ASSERT_MSG( is_pointer::value, RETURN_OPAQUE_POINTER_EXPECTS_A_POINTER_TYPE, (R)); - typedef typename mpl::if_c< - ok - , detail::opaque_conversion_holder - , detail::return_opaque_pointer_requires_a_pointer_type - >::type type; + struct type : + boost::python::to_python_value< + typename detail::value_arg::type + > + { + type() { detail::opaque_pointee(R()); } + }; }; }; + }} // namespace boost::python # endif // RETURN_OPAQUE_POINTER_HPP_ diff --git a/include/boost/python/type_id.hpp b/include/boost/python/type_id.hpp index d4fb852b..02f52ddf 100755 --- a/include/boost/python/type_id.hpp +++ b/include/boost/python/type_id.hpp @@ -14,6 +14,7 @@ # include # include # include +# include # ifndef BOOST_PYTHON_HAVE_GCC_CP_DEMANGLE # if defined(__GNUC__) \ @@ -168,6 +169,22 @@ inline char const* type_info::name() const BOOST_PYTHON_DECL std::ostream& operator<<(std::ostream&, type_info const&); +# if !BOOST_WORKAROUND(BOOST_MSVC, == 1200) +template<> +inline type_info type_id(BOOST_PYTHON_EXPLICIT_TT_DEF(void)) +{ + return type_info (typeid (void *)); +} +# ifndef BOOST_NO_CV_VOID_SPECIALIZATIONS +template<> +inline type_info type_id(BOOST_PYTHON_EXPLICIT_TT_DEF(const volatile void)) +{ + return type_info (typeid (void *)); +} +# endif + +# endif + }} // namespace boost::python #endif // TYPE_ID_DWA2002517_HPP diff --git a/test/Jamfile b/test/Jamfile index 08e1ecfc..600e9aeb 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -157,6 +157,7 @@ bpl-test crossmod_exception [ bpl-test extract ] [ bpl-test opaque ] +[ bpl-test voidptr ] [ bpl-test pickle1 ] [ bpl-test pickle2 ] diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 58a186d3..0c493070 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -110,6 +110,7 @@ bpl-test crossmod_exception [ bpl-test extract ] [ bpl-test opaque ] +[ bpl-test voidptr ] [ bpl-test pickle1 ] [ bpl-test pickle2 ] diff --git a/test/voidptr.py b/test/voidptr.py new file mode 100644 index 00000000..da1fa9c1 --- /dev/null +++ b/test/voidptr.py @@ -0,0 +1,54 @@ +# Copyright Niall Douglas 2005. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + + +""" +>>> from voidptr_ext import * + + + Check for correct conversion + +>>> use(get()) + + Check that None is converted to a NULL void pointer + +>>> useany(get()) +1 +>>> useany(None) +0 + + Check that we don't lose type information by converting NULL + opaque pointers to None + +>>> assert getnull() is None +>>> useany(getnull()) +0 + + Check that there is no conversion from integers ... + +>>> try: use(0) +... except TypeError: pass +... else: print 'expected a TypeError' + + ... and from strings to opaque objects + +>>> try: use("") +... except TypeError: pass +... else: print 'expected a TypeError' +""" +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 + status = run()[0] + if (status == 0): print "Done." + sys.exit(status)