如何通过C++;对象到另一个C++;使用Boost.Python 我有一些C++代码,定义了两个类,a和B. B在构建过程中采用了一个实例。我用Boost.Python包装了一个,这样Python就可以创建一个的实例以及子类。我想对B也这么做 A类{ 公众: A(长n,长x,长y):_n(n),_x(x),_y(y){}; long get\u n(){return\u n;} long get_x(){return_x;} long get_y(){return_y;} 私人: 长n,x,y; }; B类{ 公众: B(A)段:{}; doSomething(){…}; 私人: A_A,; };

如何通过C++;对象到另一个C++;使用Boost.Python 我有一些C++代码,定义了两个类,a和B. B在构建过程中采用了一个实例。我用Boost.Python包装了一个,这样Python就可以创建一个的实例以及子类。我想对B也这么做 A类{ 公众: A(长n,长x,长y):_n(n),_x(x),_y(y){}; long get\u n(){return\u n;} long get_x(){return_x;} long get_y(){return_y;} 私人: 长n,x,y; }; B类{ 公众: B(A)段:{}; doSomething(){…}; 私人: A_A,; };,c++,python,boost,boost-python,C++,Python,Boost,Boost Python,在包装B时,我需要了解如何将A的实例传递给B的构造函数。我做了一些挖掘,我发现最重要的是编写一个“转换器”类: struct A\u from\u python\u A{ 静态void*可转换(PyObject*obj_ptr){ //假设是,现在。。。 返回obj_ptr; } //将obj_ptr转换为实例 静态无效构造(PyObject*obj_ptr, boost::python::converter::rvalue_from_python_stage1_data*data){ //摘录

在包装B时,我需要了解如何将A的实例传递给B的构造函数。我做了一些挖掘,我发现最重要的是编写一个“转换器”类:

struct A\u from\u python\u A{
静态void*可转换(PyObject*obj_ptr){
//假设是,现在。。。
返回obj_ptr;
}
//将obj_ptr转换为实例
静态无效构造(PyObject*obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data*data){
//摘录‘n’:
PyObject*n_ptr=PyObject_CallMethod(obj_ptr,(char*)“get_n”,“char*)”()”;
长n_val=0;
如果(n_ptr==NULL){
cout=储存;
}
//寄存器转换器功能
A_来自_python_A(){
boost::python::converter::registry::push_back(
&可兑换的,
&建设,,
boost::python::type_id());
}
};
然后,我将其注册到:

BOOST\u PYTHON\u模块(插值\u扩展)
{
//从python转换器注册一个
A_from_python_A();
类(“A”,init())
;
类(“B”,init())
;
}
Convertable和Construction是分别回答“这是可转换的吗?”和“如何转换?”问题的方法方法是非平凡的——它必须进入一个py对象,提取所有相关字段,然后重建一个C++实例,然后将它传递给B的构造函数。因为A包含一些私有字段,它必须通过公共访问机制来完成(而用纯Python对象,它不必,对不对?)。这似乎是有效的。

然而,在“构造”函数中的字段抽取是否真的是必要的?看起来很费劲。如果A是一个复合对象,它可能会变得非常复杂,并且可能需要一个转换器调用另一个对象。如果A是Python类,我也许理解它的要求,但是如果A实例源自C++侧,那么有一种方法可以实现吗?确定是这种情况,然后简单地获取指向此“本机”对象的句柄(例如指针)作为快捷方式

下面是相关的python代码:

从我的外部导入A、B
a=a(1,2,3)
b=b(a)
b、 doSomething()

简而言之,将
B
的包装定义为:

class_<B>( "B", init< A >() )
包装器的定义如下:

using namespace boost::python;
BOOST_PYTHON_MODULE(example)
{
  class_< A >( "A", init< long >() )
    ;

  class_<B>( "B", init< A >() )
    .def( "doSomething", &B::doSomething )
    ;
}
这也适用于从
A
派生的类型。例如:

>>> from example import A, B
>>> class C( A ):
...     def __init__( self, n ):
...         A.__init__( self, n )
... 
>>> c = C( 2 )
>>> b = B( c )
>>> b.doSomething()
4
但是,未启用duck类型

>>> from example import A, B
>>> class E: pass
... 
>>> e = E()
>>> b = B( e )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    B.__init__(B, instance)
did not match C++ signature:
    __init__(_object*, A)
boost::python::expect_non_null
用于在返回
null
时引发异常。这有助于提供duck类型保证,python对象必须提供
get_num
方法。如果已知
PyObject
是给定类型的实例,则可以使用并直接提取类型,并避免通过
PyObject
接口进行常规调用

接下来,在模块中注册转换器

using namespace boost::python;
BOOST_PYTHON_MODULE(example)
{
  // register the from-python converter for A
  A_from_python();

  class_< A >( "A", init< long >() )
    ;

  class_<B>( "B", init< A >() )
    .def( "doSomething", &B::doSomething )
    ;
}
>>> from example import A, B
>>> a = A( 4 )
>>> b = B( a )
>>> b.doSomething()
8
>>> class D:
...     def __init__( self, n ):
...         self.n = n
...     def get_num( self ):
...         return self.n
... 
>>> d = D( 5 )
>>> b = B( d )
constructing A from <__main__.D instance at 0xb7f7340c>
>>> b.doSomething()
10
>>> class E: pass
...
>>> e = E()
>>> b = B( e )
constructing A from <__main__.E instance at 0xb7f7520c>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: get_num
D::get_num()
存在,因此当
D
被传递给
B
的构造函数时,从
D
的实例构造
A
。但是,
E::get_num()
不存在,并且尝试从
E
的实例构造
A
时引发异常


替代转换解决方案。 通过C-API实现duck类型对于较大的类型来说可能会变得非常复杂。另一种解决方案是使用python执行duck类型,并将python文件与库一起分发

example_ext.py
将导入
A
B
类型,以及monkey patch
B
的构造函数:

from example import A, B

def monkey_patch_B():
    # Store handle to original init provided by Boost.
    original_init = B.__init__

    # Construct an A object via duck-typing.
    def construct_A( obj ):
        return A( obj.get_num() )

    # Create a new init that will delegate to the original init.
    def new_init( self, obj ):
        # If obj is an instance of A, use it.  Otherwise, construct
        # an instance of A from object.
        a = obj if isinstance( obj, A ) else construct_A ( obj )

        # Delegate to the original init.
        return original_init( self, a )

    # Rebind the new_init.
    B.__init__ = new_init

monkey_patch_B()
最终用户所需的唯一更改是导入
example\u ext
,而不是导入
example

>>> from example_ext import A, B
>>> a = A( 6 )
>>> b = B( a )
>>> b.doSomething()
12
>>> class D:
...     def __init__( self, n ):
...         self.n = n
...     def get_num( self ):
...         return self.n
... 
>>> d = D( 7 )
>>> b = B( d )
>>> b.doSomething()
14
>>> class E: pass
... 
>>> e = E()
>>> b = B( e )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "example_ext.py", line 15, in new_init
    a = obj if isinstance( obj, A ) else construct_A ( obj )
  File "example_ext.py", line 9, in construct_A
    return A( obj.get_num() )
AttributeError: E instance has no attribute 'get_num'
>>来自示例_extimport A,B
>>>a=a(6)
>>>b=b(a)
>>>b.doSomething()
12
>>>D类:
…定义初始化(self,n):
…self.n=n
…def get_num(自我):
…返回自我
... 
>>>d=d(7)
>>>b=b(d)
>>>b.doSomething()
14
>>>E级:及格
... 
>>>e=e()
>>>b=b(e)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“example_ext.py”,第15行,在new_init中
a=如果存在obj(obj,a),则为obj,否则为a(obj)
文件“example_ext.py”,第9行,在构造中
返回一个(obj.get_num())
AttributeError:E实例没有属性“get\u num”
由于修补的构造函数保证将
A
的实例传递给
B
,因此不会调用python::construct中的
A\u。因此,输出中缺少print语句

虽然这种方法避免了C-API,使其更容易执行duck类型,但它确实有一个主要的折衷,即它需要对API的某些部分进行专门的修补以进行转换。另一方面,当自动类型转换功能可用时,不需要修补


也可以,在C++Python中访问控制是为了防止意外误用。既不保护对私有可见性成员的访问的故意,也更容易在Python中进行,但它通过C++模板标准在C++标准中被特别允许。这

using namespace boost::python;
BOOST_PYTHON_MODULE(example)
{
  // register the from-python converter for A
  A_from_python();

  class_< A >( "A", init< long >() )
    ;

  class_<B>( "B", init< A >() )
    .def( "doSomething", &B::doSomething )
    ;
}
>>> from example import A, B
>>> a = A( 4 )
>>> b = B( a )
>>> b.doSomething()
8
>>> class D:
...     def __init__( self, n ):
...         self.n = n
...     def get_num( self ):
...         return self.n
... 
>>> d = D( 5 )
>>> b = B( d )
constructing A from <__main__.D instance at 0xb7f7340c>
>>> b.doSomething()
10
>>> class E: pass
...
>>> e = E()
>>> b = B( e )
constructing A from <__main__.E instance at 0xb7f7520c>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: get_num
from example import A, B

def monkey_patch_B():
    # Store handle to original init provided by Boost.
    original_init = B.__init__

    # Construct an A object via duck-typing.
    def construct_A( obj ):
        return A( obj.get_num() )

    # Create a new init that will delegate to the original init.
    def new_init( self, obj ):
        # If obj is an instance of A, use it.  Otherwise, construct
        # an instance of A from object.
        a = obj if isinstance( obj, A ) else construct_A ( obj )

        # Delegate to the original init.
        return original_init( self, a )

    # Rebind the new_init.
    B.__init__ = new_init

monkey_patch_B()
>>> from example_ext import A, B
>>> a = A( 6 )
>>> b = B( a )
>>> b.doSomething()
12
>>> class D:
...     def __init__( self, n ):
...         self.n = n
...     def get_num( self ):
...         return self.n
... 
>>> d = D( 7 )
>>> b = B( d )
>>> b.doSomething()
14
>>> class E: pass
... 
>>> e = E()
>>> b = B( e )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "example_ext.py", line 15, in new_init
    a = obj if isinstance( obj, A ) else construct_A ( obj )
  File "example_ext.py", line 9, in construct_A
    return A( obj.get_num() )
AttributeError: E instance has no attribute 'get_num'