2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-26 18:52:26 +00:00

implement static data members

[SVN r18157]
This commit is contained in:
Dave Abrahams
2003-04-02 12:56:37 +00:00
parent 319a5cf97c
commit 3fd9ad7a60
5 changed files with 563 additions and 72 deletions

View File

@@ -290,19 +290,28 @@ class class_ : public objects::class_base
//
// Data member access
//
template <class D, class B>
self& def_readonly(char const* name, D B::*pm_)
template <class D>
self& def_readonly(char const* name, D const& d)
{
D T::*pm = pm_;
this->add_property(name, make_getter(pm));
return *this;
return this->def_readonly_impl(name, d, 0);
}
template <class D, class B>
self& def_readwrite(char const* name, D B::*pm_)
template <class D>
self& def_readwrite(char const* name, D const& d)
{
D T::*pm = pm_;
return this->add_property(name, make_getter(pm), make_setter(pm));
return this->def_readwrite_impl(name, d, 0);
}
template <class D>
self& def_readonly(char const* name, D& d)
{
return this->def_readonly_impl(name, d, 0);
}
template <class D>
self& def_readwrite(char const* name, D& d)
{
return this->def_readwrite_impl(name, d, 0);
}
// Property creation
@@ -334,6 +343,34 @@ class class_ : public objects::class_base
return *this;
}
template <class Get>
self& add_static_property(char const* name, Get fget)
{
base::add_static_property(
name
, object(
detail::member_function_cast<T,Get>::stage1(fget).stage2((T*)0).stage3(fget)
)
);
return *this;
}
template <class Get, class Set>
self& add_static_property(char const* name, Get fget, Set fset)
{
base::add_static_property(
name
, object(
detail::member_function_cast<T,Get>::stage1(fget).stage2((T*)0).stage3(fget)
)
, object(
detail::member_function_cast<T,Set>::stage1(fset).stage2((T*)0).stage3(fset)
)
);
return *this;
}
template <class U>
self& setattr(char const* name, U const& x)
{
@@ -362,6 +399,32 @@ class class_ : public objects::class_base
}
private: // helper functions
template <class D, class B>
self& def_readonly_impl(char const* name, D B::*pm_, int)
{
D T::*pm = pm_;
return this->add_property(name, make_getter(pm));
}
template <class D, class B>
self& def_readwrite_impl(char const* name, D B::*pm_, int)
{
D T::*pm = pm_;
return this->add_property(name, make_getter(pm), make_setter(pm));
}
template <class D>
self& def_readonly_impl(char const* name, D& d, ...)
{
return this->add_static_property(name, make_getter(d));
}
template <class D>
self& def_readwrite_impl(char const* name, D& d, ...)
{
return this->add_static_property(name, make_getter(d), make_setter(d));
}
inline void register_() const;
//

View File

@@ -18,12 +18,16 @@
# include <boost/python/detail/indirect_traits.hpp>
# include <boost/python/detail/config.hpp>
# include <boost/python/detail/wrap_python.hpp>
# include <boost/python/detail/not_specified.hpp>
# include <boost/type_traits/transform_traits.hpp>
# include <boost/type_traits/add_const.hpp>
# include <boost/type_traits/add_reference.hpp>
# include <boost/type_traits/is_same.hpp>
# include <boost/mpl/apply_if.hpp>
# include <boost/mpl/if.hpp>
# include <boost/mpl/identity.hpp>
# include <boost/bind.hpp>
@@ -71,74 +75,169 @@ namespace detail
}
};
template <class Data, class Policies>
struct datum
{
static PyObject* get(Data *p, PyObject* args_, PyObject*, Policies const& policies)
{
// find the result converter
typedef typename Policies::result_converter result_converter;
typedef typename boost::add_reference<Data>::type source;
typename mpl::apply1<result_converter,source>::type cr;
if (!policies.precall(args_)) return 0;
PyObject* result = cr( *p );
return policies.postcall(args_, result);
}
static PyObject* set(Data* p, PyObject* args_, PyObject*, Policies const& policies)
{
// check that each of the arguments is convertible
typedef typename add_const<Data>::type target1;
typedef typename add_reference<target1>::type target;
arg_from_python<target> c0(PyTuple_GET_ITEM(args_, 0));
if (!c0.convertible()) return 0;
if (!policies.precall(args_)) return 0;
*p = c0(PyTuple_GET_ITEM(args_, 0));
return policies.postcall(args_, detail::none());
}
};
template <class T>
struct default_getter_by_ref
: mpl::and_<
mpl::bool_<
to_python_value<
typename add_reference<typename add_const<T>::type>::type
>::uses_registry
>
, is_reference_to_class<
typename add_reference<typename add_const<T>::type>::type
>
>
{
};
// If it's a regular class type (not an object manager or other
// type for which we have to_python specializations, use
// return_internal_reference so that we can do things like
// x.y.z = 1
// and get the right result.
template <class T>
struct default_getter_policy
struct default_member_getter_policy
: mpl::if_<
default_getter_by_ref<T>
, return_internal_reference<>
, return_value_policy<return_by_value>
>
{};
template <class T>
struct default_datum_getter_policy
: mpl::if_<
default_getter_by_ref<T>
, return_value_policy<reference_existing_object>
, return_value_policy<return_by_value>
>
{};
template <class D, class Policies>
inline object make_getter(D* p, Policies const& policies, int)
{
typedef typename add_reference<
typename add_const<T>::type
>::type t_cref;
return objects::function_object(
::boost::bind(
&detail::datum<D,Policies>::get, p, _1, _2
, policies)
, 0);
}
BOOST_STATIC_CONSTANT(
bool, by_ref = to_python_value<t_cref>::uses_registry
&& is_reference_to_class<t_cref>::value);
template <class D>
inline object make_getter(D* p, not_specified, long)
{
typedef typename default_datum_getter_policy<D>::type policies;
return make_getter(p, policies(), 0L);
}
typedef typename mpl::if_c<
by_ref
, return_internal_reference<>
, return_value_policy<return_by_value>
>::type type;
};
}
template <class C, class D>
object make_getter(D C::*pm)
{
typedef typename detail::default_getter_policy<D>::type policy;
return objects::function_object(
::boost::bind(
&detail::member<D,C,policy>::get, pm, _1, _2
, policy())
, 1);
}
template <class C, class D, class Policies>
object make_getter(D C::*pm, Policies const& policies)
{
template <class C, class D, class Policies>
inline object make_getter(D C::*pm, Policies const& policies, int)
{
return objects::function_object(
::boost::bind(
&detail::member<D,C,Policies>::get, pm, _1, _2
, policies)
, 1);
}
template <class C, class D>
inline object make_getter(D C::*pm, not_specified, long)
{
typedef typename default_member_getter_policy<D>::type policies;
return make_getter(pm, policies(), 0L);
}
template <class D, class Policies>
inline object make_getter(D& d, Policies const& policies, ...)
{
return detail::make_getter(&d, policies, 0L);
}
template <class D, class Policies>
inline object make_setter(D* p, Policies const& policies, long)
{
return objects::function_object(
::boost::bind(
&detail::datum<D,Policies>::set, p, _1, _2
, policies)
, 1);
}
template <class C, class D, class Policies>
inline object make_setter(D C::*pm, Policies const& policies, long)
{
return objects::function_object(
::boost::bind(
&detail::member<D,C,Policies>::set, pm, _1, _2
, policies)
, 2);
}
template <class D, class Policies>
inline object make_setter(D& x, Policies const& policies, ...)
{
return detail::make_setter(&x, policies, 0L);
}
}
template <class C, class D>
object make_setter(D C::*pm)
template <class D, class Policies>
inline object make_getter(D& d, Policies const& policies)
{
return objects::function_object(
::boost::bind(
&detail::member<D,C,default_call_policies>::set, pm, _1, _2
, default_call_policies())
, 2);
return detail::make_getter(d, policies, 0L);
}
template <class C, class D, class Policies>
object make_setter(D C::*pm, Policies const& policies)
template <class D>
inline object make_getter(D& x)
{
return objects::function_object(
::boost::bind(
&detail::member<D,C,Policies>::set, pm, _1, _2
, policies)
, 2);
return detail::make_getter(x, detail::not_specified(), 0L);
}
template <class D, class Policies>
inline object make_setter(D& x, Policies const& policies)
{
return detail::make_setter(x, policies, 0L);
}
template <class D>
inline object make_setter(D& x)
{
return detail::make_setter(x, default_call_policies(), 0L);
}
}} // namespace boost::python
#endif // DATA_MEMBERS_DWA2002328_HPP

View File

@@ -41,16 +41,160 @@ instance_holder::~instance_holder()
{
}
// This is copied from typeobject.c in the Python sources. Even though
// class_metatype_object doesn't set Py_TPFLAGS_HAVE_GC, that bit gets
// filled in by the base class initialization process in
// PyType_Ready(). However, tp_is_gc is *not* copied from the base
// type, making it assume that classes are GC-able even if (like
// class_type_object) they're statically allocated.
static int
type_is_gc(PyTypeObject *python_type)
extern "C"
{
return python_type->tp_flags & Py_TPFLAGS_HEAPTYPE;
// This is copied from typeobject.c in the Python sources. Even though
// class_metatype_object doesn't set Py_TPFLAGS_HAVE_GC, that bit gets
// filled in by the base class initialization process in
// PyType_Ready(). However, tp_is_gc is *not* copied from the base
// type, making it assume that classes are GC-able even if (like
// class_type_object) they're statically allocated.
static int
type_is_gc(PyTypeObject *python_type)
{
return python_type->tp_flags & Py_TPFLAGS_HEAPTYPE;
}
// This is also copied from the Python sources. We can't implement
// static_data as a subclass property effectively without it.
typedef struct {
PyObject_HEAD
PyObject *prop_get;
PyObject *prop_set;
PyObject *prop_del;
PyObject *prop_doc;
} propertyobject;
static PyObject *
static_data_descr_get(PyObject *self, PyObject *obj, PyObject * /*type*/)
{
propertyobject *gs = (propertyobject *)self;
return PyObject_CallFunction(gs->prop_get, "()");
}
static int
static_data_descr_set(PyObject *self, PyObject *obj, PyObject *value)
{
propertyobject *gs = (propertyobject *)self;
PyObject *func, *res;
if (value == NULL)
func = gs->prop_del;
else
func = gs->prop_set;
if (func == NULL) {
PyErr_SetString(PyExc_AttributeError,
value == NULL ?
"can't delete attribute" :
"can't set attribute");
return -1;
}
if (value == NULL)
res = PyObject_CallFunction(func, "()");
else
res = PyObject_CallFunction(func, "(O)", value);
if (res == NULL)
return -1;
Py_DECREF(res);
return 0;
}
}
static PyTypeObject static_data_object = {
PyObject_HEAD_INIT(0)//&PyType_Type)
0,
"Boost.Python.class",
PyType_Type.tp_basicsize,
0,
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT // | Py_TPFLAGS_HAVE_GC
| Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, //&PyProperty_Type, /* tp_base */
0, /* tp_dict */
static_data_descr_get, /* tp_descr_get */
static_data_descr_set, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, // filled in with type_new /* tp_new */
0, // filled in with __PyObject_GC_Del /* tp_free */
(inquiry)type_is_gc, /* tp_is_gc */
};
namespace objects
{
extern "C"
{
// This declaration needed due to broken Python 2.2 headers
extern DL_IMPORT(PyTypeObject) PyProperty_Type;
}
BOOST_PYTHON_DECL PyObject* static_data()
{
if (static_data_object.tp_dict == 0)
{
static_data_object.ob_type = &PyType_Type;
static_data_object.tp_base = &PyProperty_Type;
if (PyType_Ready(&static_data_object))
return 0;
}
return upcast<PyObject>(&static_data_object);
}
}
extern "C"
{
// Ordinarily, descriptors have a certain assymetry: you can use
// them to read attributes off the class object they adorn, but
// writing the same attribute on the class object always replaces
// the descriptor in the class __dict__. In order to properly
// represent C++ static data members, we need to allow them to be
// written through the class instance. This function of the
// metaclass makes it possible.
static int
class_setattro(PyObject *obj, PyObject *name, PyObject* value)
{
// Must use "private" Python implementation detail
// _PyType_Lookup instead of PyObject_GetAttr because the
// latter will always end up calling the descr_get function on
// any descriptor it finds; we need the unadulterated
// descriptor here.
PyObject* a = _PyType_Lookup(downcast<PyTypeObject>(obj), name);
// a is a borrowed reference or 0
// If we found a static data descriptor, call it directly to
// force it to set the static data member
if (a != 0 && PyObject_IsInstance(a, objects::static_data()))
return a->ob_type->tp_descr_set(a, obj, value);
else
return PyType_Type.tp_setattro(obj, name, value);
}
}
static PyTypeObject class_metatype_object = {
@@ -72,7 +216,7 @@ static PyTypeObject class_metatype_object = {
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
class_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT // | Py_TPFLAGS_HAVE_GC
| Py_TPFLAGS_BASETYPE, /* tp_flags */
@@ -110,6 +254,8 @@ void instance_holder::install(PyObject* self) throw()
namespace objects
{
static int (*class_setattro_save)(PyObject *obj, PyObject *name, PyObject* value);
// Get the metatype object for all extension classes.
BOOST_PYTHON_DECL type_handle class_metatype()
{
@@ -119,6 +265,7 @@ namespace objects
class_metatype_object.tp_base = &PyType_Type;
if (PyType_Ready(&class_metatype_object))
return type_handle();
class_setattro_save = class_metatype_object.tp_setattro;
}
return type_handle(borrowed(&class_metatype_object));
}
@@ -189,6 +336,7 @@ namespace objects
inst->dict = python::incref(dict);
return 0;
}
}
@@ -254,6 +402,7 @@ namespace objects
class_type_object.tp_base = &PyBaseObject_Type;
if (PyType_Ready(&class_type_object))
return type_handle();
// class_type_object.tp_setattro = class_setattro;
}
return type_handle(borrowed(&class_type_object));
}
@@ -377,12 +526,6 @@ namespace objects
this->attr("__instance_size__") = instance_size;
}
extern "C"
{
// This declaration needed due to broken Python 2.2 headers
extern DL_IMPORT(PyTypeObject) PyProperty_Type;
}
void class_base::add_property(char const* name, object const& fget)
{
object property(
@@ -401,6 +544,24 @@ namespace objects
this->setattr(name, property);
}
void class_base::add_static_property(char const* name, object const& fget)
{
object property(
(python::detail::new_reference)
PyObject_CallFunction(static_data(), "O", fget.ptr()));
this->setattr(name, property);
}
void class_base::add_static_property(char const* name, object const& fget, object const& fset)
{
object property(
(python::detail::new_reference)
PyObject_CallFunction(static_data(), "OO", fget.ptr(), fset.ptr()));
this->setattr(name, property);
}
void class_base::setattr(char const* name, object const& x)
{
if (PyObject_SetAttrString(this->ptr(), const_cast<char*>(name), x.ptr()) < 0)

View File

@@ -18,7 +18,11 @@ using namespace boost::python;
typedef test_class<> X;
typedef test_class<1> Y;
struct Y : test_class<1>
{
Y(int v) : test_class<1>(v) {}
Y& operator=(Y const& rhs) { x = rhs.x; return *this; }
};
double get_fair_value(X const& x) { return x.value(); }
@@ -29,6 +33,7 @@ struct VarBase
std::string const name;
std::string get_name1() const { return name; }
};
struct Var : VarBase
@@ -38,8 +43,14 @@ struct Var : VarBase
float value;
char const* name2;
Y y;
static int static1;
static Y static2;
};
int Var::static1 = 0;
Y Var::static2(0);
BOOST_PYTHON_MODULE(data_members_ext)
{
class_<X>("X", init<int>())
@@ -74,6 +85,17 @@ BOOST_PYTHON_MODULE(data_members_ext)
.def("get_name2", &Var::get_name2, return_value_policy<return_by_value>())
.add_property("name3", &Var::get_name1)
// Test static data members
.def_readonly("ro1a", &Var::static1)
.def_readonly("ro1b", Var::static1)
.def_readwrite("rw1a", &Var::static1)
.def_readwrite("rw1b", Var::static1)
.def_readonly("ro2a", &Var::static2)
.def_readonly("ro2b", Var::static2)
.def_readwrite("rw2a", &Var::static2)
.def_readwrite("rw2b", Var::static2)
;
}

View File

@@ -1,6 +1,151 @@
'''
>>> from data_members_ext import *
---- Test static data members ---
>>> v = Var('slim shady')
>>> Var.ro2a.x
0
>>> Var.ro2b.x
0
>>> Var.rw2a.x
0
>>> Var.rw2b.x
0
>>> v.ro2a.x
0
>>> v.ro2b.x
0
>>> v.rw2a.x
0
>>> v.rw2b.x
0
>>> Var.rw2a.x = 777
>>> Var.ro2a.x
777
>>> Var.ro2b.x
777
>>> Var.rw2a.x
777
>>> Var.rw2b.x
777
>>> v.ro2a.x
777
>>> v.ro2b.x
777
>>> v.rw2a.x
777
>>> v.rw2b.x
777
>>> Var.rw2b = Y(888)
>>> Var.ro2a.x
888
>>> Var.ro2b.x
888
>>> Var.rw2a.x
888
>>> Var.rw2b.x
888
>>> v.ro2a.x
888
>>> v.ro2b.x
888
>>> v.rw2a.x
888
>>> v.rw2b.x
888
>>> v.rw2b.x = 999
>>> Var.ro2a.x
999
>>> Var.ro2b.x
999
>>> Var.rw2a.x
999
>>> Var.rw2b.x
999
>>> v.ro2a.x
999
>>> v.ro2b.x
999
>>> v.rw2a.x
999
>>> v.rw2b.x
999
>>> Var.ro1a
0
>>> Var.ro1b
0
>>> Var.rw1a
0
>>> Var.rw1b
0
>>> v.ro1a
0
>>> v.ro1b
0
>>> v.rw1a
0
>>> v.rw1b
0
>>> Var.rw1a = 777
>>> Var.ro1a
777
>>> Var.ro1b
777
>>> Var.rw1a
777
>>> Var.rw1b
777
>>> v.ro1a
777
>>> v.ro1b
777
>>> v.rw1a
777
>>> v.rw1b
777
>>> Var.rw1b = 888
>>> Var.ro1a
888
>>> Var.ro1b
888
>>> Var.rw1a
888
>>> Var.rw1b
888
>>> v.ro1a
888
>>> v.ro1b
888
>>> v.rw1a
888
>>> v.rw1b
888
>>> v.rw1b = 999
>>> Var.ro1a
999
>>> Var.ro1b
999
>>> Var.rw1a
999
>>> Var.rw1b
999
>>> v.ro1a
999
>>> v.ro1b
999
>>> v.rw1a
999
>>> v.rw1b
999
-----------------
>>> x = X(42)
>>> x.x
42
@@ -39,6 +184,7 @@
>>> v.name3
'pi'
'''
def run(args = None):