2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-21 17:12:22 +00:00

void pointer conversion support, from Niall Douglas, then heavily

edited by DWA.  Merged from python-voidptr


[SVN r32857]
This commit is contained in:
Dave Abrahams
2006-02-12 16:09:08 +00:00
parent 2d117bc4ad
commit 22e82ae30f
9 changed files with 181 additions and 124 deletions

View File

@@ -39,6 +39,12 @@
<li>New <a href="v2/docstring_options.html"
><code>docstring_options.hpp</code></a> header to
control the content of docstrings.
<li>Support for converting <code>void*</code> to/from python,
with <code><a
href="v2/opaque_pointer_converter.html">opaque_pointer_converter</a></code>
as the return value policy. Thanks to Niall Douglas for the
initial patch.
</ul>
</dd>

View File

@@ -67,8 +67,6 @@
>error C2064: term does not evaluate to a function taking 2 arguments</a>
</dt>
<dt><a href="#voidptr">How do I handle <tt>void *</tt> conversion?</a></dt>
<dt><a href="#custom_string"
>How can I automatically convert my custom string type to
and from a Python string?</a></dt>
@@ -693,29 +691,6 @@ void Export_FXThread()
.def("setAutoDelete", (bool (FXThread::*)(bool)) &amp;FXThread::setAutoDelete)</pre>
<p>(The bug has been reported to Microsoft.)</p>
<hr>
<h2><a name="voidptr"></a>How do I handle <tt>void *</tt> conversion?</h2>
<font size="-1"><i>Niall Douglas provides these notes:</i></font><p>
For several reasons Boost.Python does not support <tt>void *</tt> as
an argument or as a return value. However, it is possible to wrap
functions with <tt>void *</tt> arguments or return values using
thin wrappers and the <i>opaque pointer</i> facility. E.g.:
<pre>// 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", &amp;foo_wrapper);
}</pre>
<hr>
<h2><a name="custom_string"></a>How can I automatically
convert my custom string type to and from a Python string?</h2>

View File

@@ -9,7 +9,10 @@
# include <boost/python/converter/registrations.hpp>
# include <boost/type_traits/transform_traits.hpp>
# include <boost/type_traits/cv_traits.hpp>
# include <boost/type_traits/is_void.hpp>
# include <boost/detail/workaround.hpp>
# include <boost/python/type_id.hpp>
# include <boost/type.hpp>
namespace boost {
@@ -77,15 +80,30 @@ namespace detail
}
template <class T>
registration const&
registry_lookup(T&(*)())
inline registration const&
registry_lookup2(T&(*)())
{
detail::register_shared_ptr1((T*)0);
return registry::lookup(type_id<T&>());
}
template <class T>
registration const& registered_base<T>::converters = detail::registry_lookup((T(*)())0);
inline registration const&
registry_lookup1(type<T>)
{
return registry_lookup2((T(*)())0);
}
inline registration const&
registry_lookup1(type<const volatile void>)
{
detail::register_shared_ptr1((void*)0);
return registry::lookup(type_id<void>());
}
template <class T>
registration const& registered_base<T>::converters = detail::registry_lookup1(type<T>());
}
}}} // namespace boost::python::converter

View File

@@ -13,15 +13,22 @@
# include <boost/python/to_python_converter.hpp>
# include <boost/python/detail/dealloc.hpp>
# include <boost/python/detail/none.hpp>
# include <boost/python/type_id.hpp>
# include <boost/python/errors.hpp>
# include <boost/type_traits/remove_pointer.hpp>
# include <boost/type_traits/is_pointer.hpp>
# include <boost/type_traits/is_void.hpp>
// opaque_pointer_converter --
# include <boost/implicit_cast.hpp>
# include <boost/mpl/eval_if.hpp>
# include <boost/mpl/identity.hpp>
# include <boost/mpl/assert.hpp>
// opaque --
//
// usage: opaque_pointer_converter<Pointer>("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 <class R>
struct opaque_pointer_converter_requires_a_pointer_type
# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__)
{}
# endif
;
}
template <class Pointer>
struct opaque_pointer_converter
: to_python_converter<
Pointer, opaque_pointer_converter<Pointer> >
template <class Pointee>
struct opaque
{
BOOST_STATIC_CONSTANT(
bool, ok = is_pointer<Pointer>::value);
typedef typename mpl::if_c<
ok
, Pointer
, detail::opaque_pointer_converter_requires_a_pointer_type<Pointer>
>::type ptr_type;
private:
struct instance;
public:
explicit opaque_pointer_converter(char const* name)
opaque()
{
type_object.tp_name = const_cast<char *> (name);
lvalue_from_pytype<
opaque_pointer_converter<ptr_type>,
&opaque_pointer_converter<ptr_type>::type_object
>();
type_object.tp_name = const_cast<char*>(type_id<Pointee*>().name());
converter::registry::insert(&extract, type_id<Pointee>());
converter::registry::insert(&wrap, type_id<Pointee*>());
}
static opaque instance;
private:
static void* extract(PyObject* op)
{
return PyObject_TypeCheck(op, &type_object)
? static_cast<python_instance*>(implicit_cast<void*>(op))->x
: 0
;
}
static PyObject* convert(ptr_type x)
static PyObject* wrap(void const* px)
{
PyObject *result = 0;
Pointee* x = *static_cast<Pointee*const*>(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<PyObject*>(implicit_cast<void*>(o));
}
else
{
throw error_already_set();
}
return (result);
}
static typename ::boost::remove_pointer<ptr_type>::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 <class Pointer>
PyTypeObject opaque_pointer_converter<Pointer>::type_object =
template <class Pointee>
opaque<Pointee> opaque<Pointee>::instance;
template <class Pointee>
PyTypeObject opaque<Pointee>::type_object =
{
PyObject_HEAD_INIT(NULL)
PyObject_HEAD_INIT(0)
0,
0,
sizeof(typename opaque_pointer_converter<Pointer>::instance),
sizeof( BOOST_DEDUCED_TYPENAME opaque<Pointee>::python_instance ),
0,
::boost::python::detail::dealloc,
0, /* tp_print */
@@ -155,11 +144,14 @@ PyTypeObject opaque_pointer_converter<Pointer>::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<Pointer>::type_object =
{ \
return type_info (typeid (Pointee *)); \
} \
}}
}}
# endif
# endif // OPAQUE_POINTER_CONVERTER_HPP_

View File

@@ -10,47 +10,38 @@
# include <boost/python/detail/prefix.hpp>
# include <boost/python/opaque_pointer_converter.hpp>
# include <boost/python/detail/indirect_traits.hpp>
# include <boost/mpl/if.hpp>
# include <boost/python/detail/force_instantiate.hpp>
# include <boost/python/to_python_value.hpp>
# include <boost/python/detail/value_arg.hpp>
# include <boost/mpl/assert.hpp>
namespace boost { namespace python {
namespace detail
{
template <class Pointer>
struct opaque_conversion_holder
template <class Pointee>
static void opaque_pointee(Pointee const volatile*)
{
inline PyObject *operator()(Pointer p) const
{
static opaque_pointer_converter<Pointer> converter (
typeid (Pointer).name());
return converter.convert(p);
}
};
template <class R>
struct return_opaque_pointer_requires_a_pointer_type
# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__)
{}
# endif
;
force_instantiate(opaque<Pointee>::instance);
}
}
struct return_opaque_pointer
{
template <class R>
struct apply
{
BOOST_STATIC_CONSTANT(
bool, ok = is_pointer<R>::value);
BOOST_MPL_ASSERT_MSG( is_pointer<R>::value, RETURN_OPAQUE_POINTER_EXPECTS_A_POINTER_TYPE, (R));
typedef typename mpl::if_c<
ok
, detail::opaque_conversion_holder<R>
, detail::return_opaque_pointer_requires_a_pointer_type<R>
>::type type;
struct type :
boost::python::to_python_value<
typename detail::value_arg<R>::type
>
{
type() { detail::opaque_pointee(R()); }
};
};
};
}} // namespace boost::python
# endif // RETURN_OPAQUE_POINTER_HPP_

View File

@@ -14,6 +14,7 @@
# include <boost/static_assert.hpp>
# include <boost/detail/workaround.hpp>
# include <boost/type_traits/same_traits.hpp>
# include <boost/type_traits/broken_compiler_spec.hpp>
# 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<void>(BOOST_PYTHON_EXPLICIT_TT_DEF(void))
{
return type_info (typeid (void *));
}
# ifndef BOOST_NO_CV_VOID_SPECIALIZATIONS
template<>
inline type_info type_id<const volatile void>(BOOST_PYTHON_EXPLICIT_TT_DEF(const volatile void))
{
return type_info (typeid (void *));
}
# endif
# endif
}} // namespace boost::python
#endif // TYPE_ID_DWA2002517_HPP

View File

@@ -157,6 +157,7 @@ bpl-test crossmod_exception
[ bpl-test extract ]
[ bpl-test opaque ]
[ bpl-test voidptr ]
[ bpl-test pickle1 ]
[ bpl-test pickle2 ]

View File

@@ -110,6 +110,7 @@ bpl-test crossmod_exception
[ bpl-test extract ]
[ bpl-test opaque ]
[ bpl-test voidptr ]
[ bpl-test pickle1 ]
[ bpl-test pickle2 ]

54
test/voidptr.py Normal file
View File

@@ -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)