2013-10-19 59 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