[英]ArgumentError storing and retrieving pointer in class in Boost.Python: wrong type
I'm using Boost.Python 1.54 on Windows with MSVC2010, and I have a problem storing a pointer to one class in a second class from python, and retrieving it. 我在Windows上使用MSVC2010的Boost.Python 1.54,但在从python的第二个类中存储指向一个类的指针并检索它时遇到问题。 It seems to change data type somehow. 似乎以某种方式更改了数据类型。
Here's my classes: 这是我的课程:
typedef unsigned int uint_t;
struct classA {
int intval;
unsigned int bitfield_member:1;
};
struct Collection {
classA * class_a_ptr;
};
and here's how I expose them to python (some of this code was originally autogenerated by Py++, but I've hand-edited it since then): 这就是我将它们公开给python的方式(其中一些代码最初是由Py ++自动生成的,但从那时起我一直对其进行手工编辑):
#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/manage_new_object.hpp>
namespace bp = boost::python;
struct Collection_wrapper : Collection, bp::wrapper< Collection > {
Collection_wrapper(Collection const & arg )
: Collection( arg )
, bp::wrapper< Collection >(){
// copy constructor
}
Collection_wrapper()
: Collection()
, bp::wrapper< Collection >(){
// null constructor
}
static ::classA * get_class_a_ptr(Collection const & inst ){
return inst.class_a_ptr;
}
static void set_class_a_ptr( Collection & inst, ::classA * new_value ){
inst.class_a_ptr = new_value;
}
};
struct classA_wrapper : classA, bp::wrapper< classA > {
classA_wrapper(classA const & arg )
: classA( arg )
, bp::wrapper< classA >(){
// copy constructor
}
classA_wrapper()
: classA()
, bp::wrapper< classA >(){
// null constructor
}
::uint_t get_bitfield_member() const {
return bitfield_member;
}
void set_bitfield_member( ::uint_t new_value ){
bitfield_member = new_value;
}
};
BOOST_PYTHON_MODULE(render_lib_ext)
{
using namespace bp;
{ //::Collection
typedef bp::class_< Collection_wrapper > Collection_exposer_t;
Collection_exposer_t Collection_exposer = Collection_exposer_t( "Collection" );
bp::scope Collection_scope( Collection_exposer );
// original version, fails
Collection_exposer.add_property( "class_a_ptr"
, bp::make_function( (::classA * (*)( ::Collection const & ))(&Collection_wrapper::get_class_a_ptr), bp::return_internal_reference< >() )
, bp::make_function( (void (*)( ::Collection &,::classA * ))(&Collection_wrapper::set_class_a_ptr), bp::with_custodian_and_ward_postcall< 1, 2 >() ) );
}
{ //::classA
typedef bp::class_< classA_wrapper > classA_exposer_t;
classA_exposer_t classA_exposer = classA_exposer_t( "classA" );
bp::scope classA_scope( classA_exposer );
classA_exposer.def_readwrite( "intval", &classA::intval );
classA_exposer.add_property( "bitfield_member"
, (::uint_t ( classA_wrapper::* )( ) const)(&classA_wrapper::get_bitfield_member)
, (void ( classA_wrapper::* )( ::uint_t ) )(&classA_wrapper::set_bitfield_member) );
}
}
and here's the python test to exercise it: 这是行使它的python测试:
import unittest
import render_lib_ext as RL
class TestRenderLib(unittest.TestCase):
def test_globals(self):
coll=RL.Collection()
g = RL.classA()
g.intval=9801;
self.assertEqual(9801, g.intval)
coll.class_a_ptr = g # store pointer in collection
geg = coll.class_a_ptr # retrieve it
self.assertEqual(0, g.bitfield_member) # works
self.assertEqual(0, geg.bitfield_member) # fails with ArgumentError (type error)
self.assertEqual(9801, geg.intval) # fails! Is it not the same object?
It fails with this error on the first "fails" line: 它在第一行“失败”行上失败,并显示此错误:
Traceback (most recent call last):
File "test2.py", line 18, in test_globals
self.assertEqual(0, geg.bitfield_member) # fails with ArgumentError (type error)
ArgumentError: Python argument types in
None.None(classA)
did not match C++ signature:
None(struct classA_wrapper {lvalue})
which seems odd to me since classA_wrapper extends classA. 这对我来说很奇怪,因为classA_wrapper扩展了classA。 What am I doing wrong? 我究竟做错了什么? Is there a different way to do this? 有其他方法可以做到这一点吗? I'm pretty experienced in python and c++, but this is my first foray into Boost.Python. 我对python和c ++很有经验,但这是我对Boost.Python的首次尝试。
The functors exposed to the bitfield_member
property on classA
needs to explicitly accept the instance on which they operate. 暴露给classA
的bitfield_member
属性的函子必须明确接受其操作的实例。 It is equivalent to the property()
method in Python, where fget and fset accept the self
argument. 它等效于Python中的property()
方法,其中fget和fset接受self
参数。 Therefore, change the bitfield_member
getter and setter functions to be static and accept classA&
as their first argument. 因此,将bitfield_member
getter和setter函数更改为静态函数并接受classA&
作为其第一个参数。
// ...
struct classA_wrapper: ...
{
// ...
static ::uint_t get_bitfield_member(classA& self)
{
return self.bitfield_member;
}
static void set_bitfield_member(classA& self, ::uint_t new_value)
{
self.bitfield_member = new_value;
}
};
BOOST_PYTHON_MODULE(...)
{
namespace python = boost::python;
// ...
python::class_< classA_wrapper >("classA")
.def_readwrite("intval", &classA::intval)
.add_property("bitfield_member",
&classA_wrapper::get_bitfield_member,
&classA_wrapper::set_bitfield_member)
;
}
}
Although get_bitfield_member
and set_bitfield_member
are member functions in the original code, the Python classA
object returned from class_a_ptr
does not appear to have been completely initialized its underlying C++ type. 尽管get_bitfield_member
和set_bitfield_member
是原始代码中的成员函数,但是从class_a_ptr
返回的Python classA
对象似乎尚未完全初始化其基础C ++类型。 This may be the result of undefined behavior within the Boost.Python API. 这可能是Boost.Python API中未定义行为的结果。
The problem does not surface elsewhere, because: 该问题不会在其他地方出现,因为:
Collection.class_a_ptr
property's fget and fset explicitly accepts the instance argument. Collection.class_a_ptr
属性的fget和fset明确接受实例参数。 classA.intval
property uses def_readwrite
, which will implicitly create fget and fset that accepts the instance via make_getter/make_setter
. classA.intval
属性使用def_readwrite
,它将隐式创建fget和fset ,它们通过make_getter/make_setter
接受实例。 Here is a complete example based on the original code: 这是一个基于原始代码的完整示例:
#include <boost/python.hpp>
typedef unsigned int uint_t;
struct classA
{
int intval;
unsigned int bitfield_member:1;
};
struct Collection
{
classA * class_a_ptr;
};
namespace python = boost::python;
struct Collection_wrapper
: Collection, python::wrapper<Collection>
{
Collection_wrapper() {}
Collection_wrapper(const Collection& self)
: Collection(self)
{}
static ::classA* get_class_a_ptr(const Collection& self)
{
return self.class_a_ptr;
}
static void set_class_a_ptr(Collection& self, ::classA * new_value)
{
self.class_a_ptr = new_value;
}
};
struct classA_wrapper
: classA, python::wrapper<classA>
{
classA_wrapper() {}
classA_wrapper(const classA& self)
: classA(self)
{}
static ::uint_t get_bitfield_member(const classA& self)
{
return self.bitfield_member;
}
static void set_bitfield_member(classA& self, ::uint_t new_value)
{
self.bitfield_member = new_value;
}
};
BOOST_PYTHON_MODULE(example)
{
python::class_<Collection_wrapper>("Collection")
.add_property("class_a_ptr",
python::make_function(&Collection_wrapper::get_class_a_ptr,
python::return_internal_reference<>()),
python::make_function(&Collection_wrapper::set_class_a_ptr,
python::with_custodian_and_ward_postcall<1, 2>()))
;
python::class_<classA_wrapper>("classA")
.def_readwrite("intval", &classA::intval)
.add_property("bitfield_member",
&classA_wrapper::get_bitfield_member,
&classA_wrapper::set_bitfield_member)
;
}
And its usage: 及其用法:
>>> import example
>>> collection = example.Collection()
>>> a = example.classA()
>>> a.intval = 9801
>>> print a.intval
9801
>>> collection.class_a_ptr = a
>>> same_a = collection.class_a_ptr
>>> a.bitfield_member = 0
>>> print a.bitfield_member
0
>>> print same_a.bitfield_member
0
>>> same_a.bitfield_member = 1
>>> print a.bitfield_member
1
>>> print same_a.bitfield_member
1
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.