代码之家  ›  专栏  ›  技术社区  ›  G.Hovhannisyan

如何使python在通过boost::python对std::vector进行迭代期间不从boost::shared\u ptr创建/复制pyobject?

  •  1
  • G.Hovhannisyan  · 技术社区  · 7 年前

    这里的问题是,在python中公开向量的迭代过程中,我每次都会为同一个对象获得不同的地址。 据我所知,python在每次迭代时通过我的boost::shared\u ptr创建pyobject,迭代后(实际上是在第二次迭代之后)垃圾收集器释放该地址,尽管元素是boost::shared\u ptr-s。 当元素在python的列表中时,它们具有在迭代过程中不变的唯一地址。这就是我想要实现的。 我试图通过在stackoverflow中访问不同的boost文档和不同的QA来上网,但没有解决我的问题。 我创建了一个小示例来演示这个问题。 以下是C++代码:

    #include <vector>
    #include <boost/shared_ptr.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/python.hpp>
    
    namespace bp = boost::python;
    
    class A {
        public:
            A() : m_int(0) { }
            A(int numb) : m_int(numb) { }
            int get_m() const { return m_int; }
        private:
            int m_int;
    };
    
    std::ostream& operator<<(std::ostream& out, const A& obj) {
        out << obj.get_m();
        return out;
    }
    
    using APtr = boost::shared_ptr<A>;
    using ACont = std::vector<APtr>;
    using AContPtr = boost::shared_ptr<ACont>;
    
    APtr CreateA() {
        return boost::make_shared<A>();
    }
    
    APtr CreateAFromNumb(int numb) {
        return boost::make_shared<A>(numb);
    }
    
    AContPtr CreateEmptyACont() {
        return boost::make_shared<ACont>();
    }
    
    AContPtr CreateFullACont(int size) {
        ACont vec{};
        for (int i = 0; i < size; ++i) {
            vec.push_back(boost::make_shared<A>(i));
        }
        return boost::make_shared<ACont>(vec);
    }
    
    BOOST_PYTHON_MODULE(example) {
        bp::class_<A, APtr, boost::noncopyable>("A", bp::no_init)
            .def("__init__", bp::make_constructor(CreateA))
            .def("__init__", bp::make_constructor(CreateAFromNumb))
            .def(bp::self_ns::str(bp::self))
            ;
        bp::register_ptr_to_python<APtr>();
    
        bp::class_<ACont, AContPtr, boost::noncopyable>("ACont", bp::no_init)
            .def("__init__", bp::make_constructor(CreateEmptyACont))
            .def("__init__", bp::make_constructor(CreateFullACont))
            .def("__iter__", bp::iterator<ACont>())
            .def("__len__", &ACont::size)
            ;
        bp::register_ptr_to_python<AContPtr>();
    }
    

    下面是它在python中的用法:

    #!/usr/bin/env python
    
    from example import *
    
    vec = ACont(4)
    
    print len(vec)
    print "VEC", vec
    for x in vec:
        print x,
        print repr(x)
    
    print "VEC", vec
    for x in vec:
        print x,
        print repr(x)
    
    l = [x for x in vec]
    
    print "LIST"
    for x in l:
        print x,
        print repr(x)
    print "LIST"
    for x in l:
        print x,
        print repr(x)
    

    在暴露的std::vector/boost::shared\u ptr上迭代时<std::矢量>我在两个连续的迭代中得到了相同元素的不同地址。

    1. Python在第一次迭代时为第一个项创建pyobject,并 显示其地址。
    2. 在第二次迭代中,python为另一个地址上的第二个元素创建另一个pyobject。
    3. 垃圾收集器发现第一个地址不再可用,并将其释放。
    4. 在第三次迭代中,python在与第一个元素相同的地址中为第三个元素创建另一个pyobject,因为gc刚刚释放了它。
    5. 等等

    但是对相同元素列表的迭代是正确的,因为python不会创建/破坏pyobjects,因为它们在列表本身中有引用。 以下是python运行的输出:

    4
    VEC <example.ACont object at 0x7ffff7ea9578>
    0 <example.A object at 0x7ffff7eb27d0>    # address 1
    1 <example.A object at 0x7ffff7eb2848>    # address 2
    2 <example.A object at 0x7ffff7eb27d0>    # address 1 since gc just have freed it
    3 <example.A object at 0x7ffff7eb2848>    # so on
    VEC <example.ACont object at 0x7ffff7ea9578>
    0 <example.A object at 0x7ffff7eb27d0>
    1 <example.A object at 0x7ffff7eb2848>
    2 <example.A object at 0x7ffff7eb27d0>
    3 <example.A object at 0x7ffff7eb2848>
    LIST
    0 <example.A object at 0x7ffff7eb27d0>    # address 1
    1 <example.A object at 0x7ffff7eb2848>    # address 2
    2 <example.A object at 0x7ffff7eb28c0>    # address 3
    3 <example.A object at 0x7ffff7eb2938>    # address 4
    LIST
    0 <example.A object at 0x7ffff7eb27d0>
    1 <example.A object at 0x7ffff7eb2848>
    2 <example.A object at 0x7ffff7eb28c0>
    3 <example.A object at 0x7ffff7eb2938>
    

    总之,考虑到这些对象是某些对象的boost::shared\u ptr-s,我想知道是否有一种方法不在python中为C++中创建的每个元素创建pyobjects。 提前谢谢你。

    P、 我正在使用boost版本1.60和python 2.7(anaconda包)。

    1 回复  |  直到 7 年前
        1
  •  0
  •   G.Hovhannisyan    7 年前

    因此,经过长时间的调查,我发现我想要解决的任务并没有真正的解决方案。这是因为python必须注册每个刚刚出现在其环境中的对象(在其他地方创建的,而不是在python中创建的),以了解它。因此,场景的其余部分,关于垃圾收集器,如问题中所述。boost/python和swig都无法处理这种情况。
    此外,由于我需要处理地址的哈希功能,我在那里也做了同样的事情。这是我的最新消息

    // Same code as previously
    std::string GetAddress(const APtr& a_ptr) {
        std::string repr{"<example.A object at "};
        std::stringstream sstream;
        sstream << std::hex << std::showbase << reinterpret_cast<std::uintptr_t>(a_ptr.get());
        repr += sstream.str() + ">";
        return repr;
    }
    
    static std::size_t Hash(const APtr& a_ptr) {
        std::hash<std::uintptr_t> hasher;
        return hasher(reinterpret_cast<std::uintptr_t>(a_ptr.get()));
    }
    
    
    BOOST_PYTHON_MODULE(example) {
        bp::class_<A, APtr, boost::noncopyable>("A", bp::no_init)
            .def("__init__", bp::make_constructor(CreateA))
            .def("__init__", bp::make_constructor(CreateAFromNumb))
            .def("__repr__", GetAddress)
            .def("__hash__", Hash)
            .def(bp::self_ns::str(bp::self))
            ;
        bp::register_ptr_to_python<APtr>();
    
        bp::class_<ACont, AContPtr, boost::noncopyable>("ACont", bp::no_init)
            .def("__init__", bp::make_constructor(CreateEmptyACont))
            .def("__init__", bp::make_constructor(CreateFullACont))
            .def("__iter__", bp::iterator<ACont>())
            .def("__len__", &ACont::size)
            ;
        bp::register_ptr_to_python<AContPtr>();
    }
    

    无论如何,感谢你们所有人的努力帮助。:)