2016-03-24 82 views
0

我试图让所以这段代码返回相同的实例中都初始化函数和回调函数升压蟒蛇返回同一个实例与make_constructor

test1.py

import test1 

c = test1.C() 
print 'init:', c 

def func(c): 
    print 'func:', c 

test1.register_callback(func) 

test1.cpp

#include <iostream> 
#include <vector> 

#include <boost/python.hpp> 

using namespace boost::python; 

class C; 
std::vector<boost::shared_ptr<C>> v; 

class C 
: public boost::noncopyable 
{ 
public: 
    C() { 
     std::cout << "C()" << std::endl; 
    } 

    ~C() { 
     std::cout << "~C()" << std::endl; 
    } 
}; 

boost::shared_ptr<C> create() { 
    C *c = new C(); 
    auto ptr = boost::shared_ptr<C>(c); 
    v.push_back(ptr); 
    return ptr; 
} 

void register_callback(object func) { 
    func(v[0]); 
} 

BOOST_PYTHON_MODULE(test1) 
{ 
    class_<C, boost::shared_ptr<C>, boost::noncopyable>("C", no_init) 
     .def("__init__", make_constructor(&create)) 
    ; 

    def("register_callback", register_callback); 
} 

我现在得到的输出是:

init: <test1.C object at 0x7f62181bd5d0> 
func: <test1.C object at 0x7f62181c1848> 

什么我想要得到的是:

init: <test1.C object at 0x7f62181bd5d0> 
func: <test1.C object at 0x7f62181bd5d0> 

这是可能的,如何?

回答

2

当Python构造的对象时,将__init__与表示该对象的第一参数调用被构造,通常称为self。 Boost.Python试图尽可能隐藏这个论点,只是在某些条件下将其暴露给init-expression。从boost::python::make_constructor()返回的可调用Python对象知道此第一个参数,但没有自定义点将它转发给包装函数。一种解决方案是,以暴露一个C++函数为__init__boost::python::make_function()接受所有的参数给Python提供,包括self,然后委托给从boost::python::make_constructor()返回的算符:

... 
std::vector<boost::python::object> v; 

void create(boost::python::object self) 
{ 
    // Create a constructor object. In this case, a lambda 
    // casted to a function is being used. 
    auto constructor = boost::python::make_constructor(+[]() { 
    return boost::make_shared<C>(); 
    }); 

    // Invoke the constructor. 
    constructor(self); 

    // If construction does not throw, then store a reference to self. 
    v.push_back(self); 
} 

... 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 
    python::class_<C, boost::shared_ptr<C>, boost::noncopyable>(
     "C", python::no_init) 
    .def("__init__", python::make_function(&create)) 
    ; 

    ... 
} 

下面是一个完整例如demonstrating这种方法:

#include <boost/python.hpp> 
#include <vector> 
#include <boost/make_shared.hpp> 

class C: public boost::noncopyable {}; 
std::vector<boost::python::object> v; 

template <typename ...Args> 
void create(boost::python::object self, Args&&... args) 
{ 
    // Create a constructor object. 
    auto constructor = boost::python::make_constructor(
    +[](Args&&...args) { 
    return boost::make_shared<C>(std::forward<Args>(args)...); 
    }); 

    // Invoke the constructor. 
    constructor(self, std::forward<Args>(args)...); 

    // If construction does not throw, then store a reference to self. 
    v.push_back(self); 
} 

void register_callback(boost::python::object func) 
{ 
    func(v[0]); 
} 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 
    python::class_<C, boost::shared_ptr<C>, boost::noncopyable>(
     "C", python::no_init) 
    .def("__init__", python::make_function(&create<>)) 
    ; 

    python::def("register_callback", &register_callback); 
} 

交互式用法:

>>> import example 
>>> c1 = example.C() 
>>> print 'init:', c1 
init: <example.C object at 0x7f12f425d0a8> 
>>> c2 = None 
>>> def func(c): 
...  global c2 
...  print 'func:', c 
...  c2 = c 
... 
>>> example.register_callback(func) 
func: <example.C object at 0x7f12f425d0a8> 
>>> assert(c1 is c2) 
+0

这是正确的方法,谢谢! – Markus

0

它已经是相同的实例里面的相同shared_ptr<C>。您可以轻松验证这一点。

问题是,您将根据此shared_ptr<C>创建两个不同的py::object s:一个来自create(),另一个来自register_callback()。那些必然会是不同的对象,它们没有任何关系。

简单的解决方案是不使用make_constructor。创建一个工厂函数:

py::object makeC() { 
    C *c = new C(); 
    auto ptr = py::object{boost::shared_ptr<C>(c)}; 
    v.push_back(ptr); // store as objects, since you need them to be the same 
    return ptr; 
} 

void register_callback(object func) { 
    func(v[0]); 
} 

BOOST_PYTHON_MODULE(test1) 
{ 
    class_<C, boost::shared_ptr<C>, boost::noncopyable>("C", no_init) 
     /* actually no_init for real */ 
    ; 

    def("makeC", makeC); 
    def("register_callback", register_callback); 
} 
+0

是的我知道它在向量中的同一个实例,但是如何在两种方式中返回相同的py :: object? – Markus

+0

@Markus你为什么需要这个? – Barry

+0

这是一个基于插件的系统,插件可以在python中创建一个实例,例如c = C(); c.some_variable = 10;那么另一个插件会在已注册的回调中获取此实例,如果它不是同一个对象,则新创建的some_variable显然不存在。我用一个丑陋的黑客修复了它,但它并没有做出一个干净的好的API,所以我更喜欢C()返回同一个对象,或者说我可以返回一个对象,将它保存在某个地方,并重用同一个对象我做了回调,但make_constructor无法返回py :: object – Markus