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)