2012-07-02 42 views
3

我有一個類Type它不能複製,也不包含默認的構造函數。 我有第二類A作爲一組上述類。這第二類通過迭代器可以訪問我的迭代器引用操作:如何使用boost :: python :: iterator和return_internal_reference?

class A { 
    class iterator { 
     [...] 
     public: 
     Type & operator*() 
     { 
      return instance; 
     } 
     private: 
     Type instance; 
    } 
    [...] 
}; 

現在揭露,我寫了一個boost::python代碼,看起來像這樣:

class_<A>("A", [...]) 
    .def("__iter__", iterator<A, return_internal_reference<> >()) 
    .def("__len__", container_length_no_diff<A, A::iterator>) 
; 

添加打印消息發送給所有迭代操作後(建設,分配,指針引用,破壞)代碼的Python這樣的:

for o in AInstance: 
    print o.key 

我得到的輸出(修剪到重要組成部分):

construct 0xffffffff7fffd3e8 
dereference: 0xffffffff7fffd3e8 
destroy 0xffffffff7fffd3e8 
get key 0xffffffff7fffd3e8 

在上面的代碼中的那些地址只是instance構件(或者在方法調用this)的地址。 前三行由iterator生成,第四行由Type中的getter方法打印。所以不知何故boost::python包裝在這樣的方式的一切,它:

  1. 創建迭代
  2. 解除引用迭代器和存儲基準
  3. 破壞迭代器(和對象包含)
  4. 使用在步驟2
  5. 獲得的參考

很清楚return_internal_reference不像表述那樣(請注意,它實際上只是typedef ove r with_custodian_and_ward_postcall<>)只要方法調用的結果被引用,它應該保持對象。

所以我的問題是如何將這樣一個迭代器暴露給Python boost::python

編輯:

正如有人指出,它可能不是很清楚:原來的容器中不含Type類型的對象。它包含一些我能夠構造/修改對象的對象。所以iterator在上面的例子中就像transform_iterator一樣。

回答

1

我認爲整個問題是我沒有完全明白iterator類應該提供什麼語義。 似乎只要容器存在,迭代器返回的值必須是有效的,而不是迭代器。

這意味着boost::python正確的行爲,並有兩種解決方案,即:

  • 使用boost::shared_ptr
  • 返回由值

少一點有效的方法比我試圖做的,但看起來沒有其他辦法。

編輯: 我已經研究出瞭解決辦法(不僅是可能的,但它似乎很好地工作):Boost python container, iterator and item lifetimes

1

如果A是擁有的Type實例的容器中,然後再考慮其A::iterator包含句柄Type而不是有Type

class iterator { 
    [...] 
private: 
    Type* instance; // has a handle to a Type instance. 
}; 

相反的:

class iterator { 
    [...] 
private: 
    Type instance; // has a Type instance. 
}; 

在python中,迭代器將包含對它迭代的容器的引用。這將延長可迭代對象的生命週期,並防止在迭代過程中迭代對象被垃圾收集。

>>> from sys import getrefcount 
>>> x = [1,2,3] 
>>> getrefcount(x) 
2 # One for 'x' and one for the argument within the getrefcount function. 
>>> iter = x.__iter__() 
>>> getrefcount(x) 
3 # One more, as iter contains a reference to 'x'. 

boost::python支持此行爲。這是一個示例程序,其中Foo是一個不能複製的簡單類型; FooContainer是一個可迭代的容器;和FooContainer::iterator是一個迭代:

#include <boost/python.hpp> 
#include <iterator> 

// Simple example type. 
class Foo 
{ 
public: 
    Foo() { std::cout << "Foo constructed: " << this << std::endl; } 
    ~Foo() { std::cout << "Foo destroyed: " << this << std::endl; } 
    void set_x(int x) { x_ = x; } 
    int get_x()  { return x_; } 
private: 
    Foo(const Foo&);   // Prevent copy. 
    Foo& operator=(const Foo&); // Prevent assignment. 
private: 
    int x_; 
}; 

// Container for Foo objects. 
class FooContainer 
{ 
private: 
    enum { ARRAY_SIZE = 3 }; 
public: 
    // Default constructor. 
    FooContainer() 
    { 
    std::cout << "FooContainer constructed: " << this << std::endl; 
    for (int i = 0; i < ARRAY_SIZE; ++i) 
    { 
     foos_[ i ].set_x((i + 1) * 10); 
    } 
    } 

    ~FooContainer() 
    { 
    std::cout << "FooContainer destroyed: " << this << std::endl; 
    } 

    // Iterator for Foo types. 
    class iterator 
    : public std::iterator< std::forward_iterator_tag, Foo > 
    { 
    public: 
     // Constructors. 
     iterator()      : foo_(0)  {} // Default (empty). 
     iterator(const iterator& rhs) : foo_(rhs.foo_) {} // Copy. 
     explicit iterator(Foo* foo)  : foo_(foo)  {} // With position. 

     // Dereference. 
     Foo& operator*() { return *foo_; } 

     // Pre-increment 
     iterator& operator++() { ++foo_; return *this; } 
     // Post-increment.  
     iterator operator++(int) 
     { 
     iterator tmp(foo_); 
     operator++(); 
     return tmp; 
     } 

     // Comparison. 
     bool operator==(const iterator& rhs) { return foo_ == rhs.foo_; } 
     bool operator!=(const iterator& rhs) 
     { 
     return !this->operator==(rhs); 
     } 

    private: 
     Foo* foo_; // Contain a handle to foo; FooContainer owns Foo. 
    }; 

    // begin() and end() are requirements for the boost::python's 
    // iterator<container> spec. 
    iterator begin() { return iterator(foos_);    } 
    iterator end() { return iterator(foos_ + ARRAY_SIZE); } 
private: 
    FooContainer(const FooContainer&);   // Prevent copy. 
    FooContainer& operator=(const FooContainer&); // Prevent assignment. 
private: 
    Foo foos_[ ARRAY_SIZE ]; 
}; 

BOOST_PYTHON_MODULE(iterator_example) 
{ 
    using namespace boost::python; 
    class_< Foo, boost::noncopyable >("Foo") 
    .def("get_x", &Foo::get_x) 
    ; 
    class_< FooContainer, boost::noncopyable >("FooContainer") 
    .def("__iter__", iterator< FooContainer, return_internal_reference<> >()) 
    ; 
} 

這裏是示例輸出:

>>> from iterator_example import FooContainer 
>>> fc = FooContainer() 
Foo constructed: 0x8a78f88 
Foo constructed: 0x8a78f8c 
Foo constructed: 0x8a78f90 
FooContainer constructed: 0x8a78f88 
>>> for foo in fc: 
... print foo.get_x() 
... 
10 
20 
30 
>>> fc = foo = None 
FooContainer destroyed: 0x8a78f88 
Foo destroyed: 0x8a78f90 
Foo destroyed: 0x8a78f8c 
Foo destroyed: 0x8a78f88 
>>> 
>>> fc = FooContainer() 
Foo constructed: 0x8a7ab48 
Foo constructed: 0x8a7ab4c 
Foo constructed: 0x8a7ab50 
FooContainer constructed: 0x8a7ab48 
>>> iter = fc.__iter__() 
>>> fc = None 
>>> iter.next().get_x() 
10 
>>> iter.next().get_x() 
20 
>>> iter = None 
FooContainer destroyed: 0x8a7ab48 
Foo destroyed: 0x8a7ab50 
Foo destroyed: 0x8a7ab4c 
Foo destroyed: 0x8a7ab48 
+0

所以有點相同,我想出了自己。另外純粹的'Type * instance;'將很難追蹤到我認爲的某種共享指針是更好的方法。特別是'boost :: python'支持(幾乎?)沒有努力。 此外,這種方法有一個缺點是由於分配速度變慢,所以我最終選擇了實現拷貝構造函數並按值返回(儘管我必須做一些測試來確定它比分配指針數據更快)。 – elmo

+0

我想你完全錯過了這一點。我有一些'transform_iterator'。我想返回的對象實例不存在於任何地方。 Container不包含'Type'對象,但是允許我構造'Type'。 – elmo

+0

@elmo:您可能需要考慮更新原始問題以反映這些需求/意圖,因爲此時沒有提及'transform_iterator',並且存在包含您想要返回的'Type'對象的建議在'A'內。 –

0

下面是有關樣品: https://wiki.python.org/moin/boost.python/iterator
你纔可以由const /非const的返回迭代器值參考

... 
.def("__iter__" 
    , range<return_value_policy<copy_non_const_reference> >(
      &my_sequence<heavy>::begin 
     , &my_sequence<heavy>::end)) 

的想法是,正如你所說,我們應該綁定到容器的壽命,而不是迭代器壽命的返回值。