2013-10-19 58 views
1

我在Windows上使用Boost.Python 1.54和MSVC2010,并且存在一个指向第二个类中的一个类的指针,它。它似乎改变了数据类型。ArgumentError在Boost.Python类中存储和检索类中的指针:错误类型

这里是我的课:

typedef unsigned int uint_t; 

struct classA { 
    int intval; 
    unsigned int bitfield_member:1; 
}; 

struct Collection { 
    classA * class_a_ptr; 
}; 

这里就是我揭露他们的蟒蛇(一些这个代码最初是由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)); 
    } 
} 

和这里的蟒蛇测试行使它:

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? 

它失败,并在此错误第一个“失败”行:

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

这对我来说似乎很奇怪,因为classA_wrapper扩展了classA。我究竟做错了什么?有没有不同的方式来做到这一点?我在python和C++方面非常有经验,但这是我第一次进入Boost.Python。

回答

1

classA上暴露于bitfield_member属性的仿函数需要明确接受它们在其上运行的实例。它相当于Python中的property()方法,其中fgetfset接受self的说法。因此,将bitfield_member getter和setter函数更改为static,并接受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) 
    ; 
    } 
} 

get_bitfield_member虽然和set_bitfield_member是在原始代码中的成员函数,Python的classA对象从class_a_ptr返回不显示已完全初始化其底层C++型。这可能是Boost.Python API中未定义行为的结果。

该问题不会在其他地方浮出水面,这是因为:

  • Collection.class_a_ptr酒店的fgetFSET明确接受instance参数。
  • classA.intval属性使用def_readwrite,这将隐式创建fgetFSET经由make_getter/make_setter接受实例。

这是在原有基础上的代码一个完整的例子:

#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) 
    ; 
} 

及其用法:

>>> 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 
+0

谢谢!这看起来完全正确。在我的情况下,我有太多的实际成员来手动编辑所有Py ++生成的访问器,所以相反,我让'get_class_a_ptr'返回'classA_wrapper *'而不是'classA *',这似乎也行得通。 – GaryO