ArgumentError 在 Boost.Python 中的类中存储和检索指针:错误的类型

ArgumentError storing and retrieving pointer in class in Boost.Python: wrong type

本文关键字:检索 指针 类型 错误 Boost Python ArgumentError 存储      更新时间:2023-10-16

我在带有MSVC2010的Windows上使用Boost.Python 1.54,我在python的第二个类中存储指向一个类的指针并检索它时遇到了问题。 它似乎以某种方式更改了数据类型。

这是我的课程:

typedef unsigned int uint_t;
struct classA {
  int intval;
  unsigned int bitfield_member:1;
};
struct Collection {
  classA * class_a_ptr;
};

以下是我将它们公开给 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) );
  }
}

这是练习它的 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?

它在第一个"失败"行上失败并显示此错误:

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扩展了 A 类。 我做错了什么? 有没有不同的方法可以做到这一点? 我在python和c ++方面很有经验,但这是我第一次涉足Boost.Python。

classA上向 bitfield_member 属性公开的函子需要显式接受它们操作的实例。 它等效于 Python 中的 property() 方法,其中 fgetfset 接受 self 参数。 因此,将 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)
    ;
  }
}

尽管get_bitfield_memberset_bitfield_member是原始代码中的成员函数,但从class_a_ptr返回的 Python classA 对象似乎尚未完全初始化其基础C++类型。 这可能是 Boost.Python API 中未定义行为的结果。

该问题不会在其他地方出现,因为:

  • Collection.class_a_ptr属性的 fgetfset 显式接受实例参数。
  • classA.intval属性使用 def_readwrite ,这将隐式创建通过 make_getter/make_setter 接受实例的 fgetfset

下面是一个基于原始代码的完整示例:

#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