2011-11-22 24 views
6

Tôi muốn lưu trữ tham chiếu đến đối tượng dưới dạng weak_ptr. Trong tinh khiết C++, các công việc sau:boost :: python and weak_ptr: công cụ biến mất

#include <iostream> 

#include <boost/shared_ptr.hpp> 
#include <boost/weak_ptr.hpp> 

using namespace std; 
using namespace boost; 

struct Empty 
{ 
    Empty(){} 
}; 

struct Store 
{ 
    weak_ptr<Empty> value; 
    Store(){}; 

    void setValue(shared_ptr<Empty> v) { 
     cout << "storing " << v << endl; 
     this->value = weak_ptr<Empty>(v); 
     shared_ptr<Empty> v_ok = this->value.lock(); 
     if (v_ok) { 
      cout << "ok, v has been stored" << endl; 
     } 
    } 

    shared_ptr<Empty> getValue() { 
     shared_ptr<Empty> p = this->value.lock(); 
     if (p) { 
      cout << "stored value : " << p << endl; 
     } else { 
      cout << "there's nothing here !" << endl; 
     } 
     return p; 
    } 
}; 

int main() 
{ 
    shared_ptr<Empty> e(new Empty); 
    shared_ptr<Store> st(new Store); 

    st->setValue(e); 
    st->getValue(); 
    return 0; 
} 

biên dịch và chạy này sẽ cung cấp cho bạn điều này:

%> ./a.out 
storing 0x8c6c008 
ok, v has been stored 
stored value : 0x8c6c008 

Bây giờ, nếu tôi tóm lược rằng với tăng python:

#include <iostream> 

#include <boost/shared_ptr.hpp> 
#include <boost/python.hpp> 
#include <boost/weak_ptr.hpp> 

using namespace std; 
using namespace boost; 
using namespace boost::python; 

struct Empty 
{ 
    Empty(){} 
}; 

struct Store 
{ 
    weak_ptr<Empty> value; 
    Store(){}; 

    void setValue(shared_ptr<Empty> v) { 
     cout << "storing " << v << endl; 
     this->value = weak_ptr<Empty>(v); 
     shared_ptr<Empty> v_ok = this->value.lock(); 
     if (v_ok) { 
      cout << "ok, v has been stored" << endl; 
     } 
    } 

    shared_ptr<Empty> getValue() { 
     shared_ptr<Empty> p = this->value.lock(); 
     if (p) { 
      cout << "stored value : " << p << endl; 
     } else { 
      cout << "there's nothing here !" << endl; 
     } 
     return p; 
    } 
}; 

BOOST_PYTHON_MODULE (test) 
{ 
    class_< Empty, shared_ptr<Empty> >("Empty"); 

    class_< Store, shared_ptr<Store> >("Store") 
    .def("get",&Store::getValue) 
    .def("set",&Store::setValue); 
} 

và bây giờ một tập lệnh python nhỏ để thử nó ra

from test import * 

e = Empty() 
st = Store() 

st.set(e) 
st.get() 

... và kết quả là ...

storing 0x9eb2a18 
ok, v has been stored 
there's nothing here ! 

như vậy rõ ràng trong khi tôi vẫn còn trong cùng một phương pháp (setValue), không có vấn đề khi truy một shared_ptr từ Cửa hàng :: giá trị. Nhưng ngay sau khi tôi thoát khỏi bối cảnh này, không còn gì nữa!

Làm cách nào để thực hiện điều này? Là python đi qua một shared_ptr mới (và vô dụng) như là một đối số để setValue, mà sau đó bị phá hủy vào cuối cuộc gọi? Tôi bị lạc ở đây.

+1

Hmm ... Thêm trình phá hủy vào 'Empty' để in thông báo và xem nó có bị hủy hay không. Ngoài ra, điều gì xảy ra nếu bạn lưu trữ một 'shared_ptr'? –

+1

Đoán của tôi là ở một vị trí 'shared_ptr' dùng để chỉ' :: boost :: shared_ptr' và trong một cái khác nó đề cập đến ':: std :: shared_ptr'. Tôi rất thích sử dụng 'typedef' để lấy các tên từ các không gian tên khác nhau để có nhiều khai báo' using namespace'. – Omnifarious

+1

Hãy nghĩ rằng nó liên quan đến điều này? http://mail.python.org/pipermail/cplusplus-sig/2009-November/014981.html – HostileFork

Trả lời

4

Điều này cực kỳ tò mò. Tôi đã loại trừ khả năng std so với boost pointer, và chơi xung quanh với một vài kiểm tra sanity, và theo như tôi có thể nói đó là một cái gì đó mà tăng python đang làm cho con trỏ chia sẻ mà phá vỡ nó.

Truy tìm trình tạo đối tượng/trình phá hủy, thời gian tồn tại của Empty và Store đang được quản lý như bạn mong đợi (không có bản sao nào xảy ra).

Một cực kỳ thú vị Cái này là shared_from_this tiếp tục làm việc, ngay cả khi weak_ptr<>.lock() không, và, trên thực tế, một con trỏ yếu mới được tạo ra từ một con trỏ được chia sẻ mới (từ shared_from_this) không làm việc.

Vì vậy, điều này dẫn tôi đến the thread linked in the comments, có vẻ như có điều gì đó thúc đẩy python đang làm với deleter và số tham chiếu đó là phá vỡ con trỏ yếu.

Kiểm tra các con trỏ được chia sẻ trong chương trình gỡ rối đây là những gì chúng tôi nhận được:

Khi chúng ta gọi là setValue, đây là những gì đối số trông giống như:

1: p = (const 'boost::shared_ptr<Empty>' &) @0x7fff5fbfe720: { 
    px = 0x100346900, 
    pn = { 
    pi_ = 0x100338dd0 
    } 
} 
> p *p.pn.pi_ 
$5 = (boost::detail::sp_counted_impl_pd<void*,boost::python::converter::shared_ptr_deleter>) { 
    <boost::detail::sp_counted_base> = { 
    _vptr$sp_counted_base = 0x10061aa30, 
    use_count_ = 2, 
    weak_count_ = 2 
    }, 
    members of boost::detail::sp_counted_impl_pd<void*,boost::python::converter::shared_ptr_deleter>: 
    ptr = 0x0, 
    del = { 
    owner = { 
     m_p = 0x10049db90 
    } 
    } 
} 

Nếu chúng ta tạo ra một con trỏ chia sẻ sử dụng shared_from_this về đối số, có vẻ như sau:

1: p = (const 'boost::shared_ptr<Empty>' &) @0x7fff5fbfe5e0: { 
    px = 0x100346900, 
    pn = { 
    pi_ = 0x1003468e0 
    } 
} 
> p *p.pn.pi_ 
$4 = (boost::detail::sp_counted_impl_pd<Empty*,boost::detail::sp_ms_deleter<Empty> >) { 
    <boost::detail::sp_counted_base> = { 
    _vptr$sp_counted_base = 0x10061b170, 
    use_count_ = 2, 
    weak_count_ = 2 
    }, 
    members of boost::detail::sp_counted_impl_pd<Empty*,boost::detail::sp_ms_deleter<Empty> >: 
    ptr = 0x0, 
    del = { 
    initialized_ = true, 
    storage_ = { 
     data_ = "\000i4\000\001\000\000\000?h4\000\001\000\000", 
     align_ = {<No data fields>} 
    } 
    } 
} 

Có một điều không te ở đây: địa chỉ của số chia sẻ là khác nhau: đây là một cá thể chia sẻ khác nhau ... vì vậy chúng tôi đã tạo ra hai con trỏ được chia sẻ khác nhau cho cùng một địa chỉ. Đây là một điều cực kỳ tồi tệ, vì chúng tôi hy vọng điều này sẽ không mang lại kết quả gấp đôi.

Tuy nhiên, không. (Tôi khá muốn hiểu thêm về điều này, nếu có ai có ý tưởng gì?)

Tôi không biết tại sao nó không thành thật (có lẽ có điều gì đó tinh tế đang diễn ra ở đây), nhưng trong mọi trường hợp, tất cả điều này chỉ ra giải pháp: Chúng tôi có thể sử dụng shared_from_this để tạo con trỏ được chia sẻ, từ giá trị được truyền, mà chúng tôi có thể sử dụng để tạo con trỏ yếu thực sự hoạt động.

Vì vậy, để quấn lên, đây là việc sửa chữa:

#include <iostream> 
#include <boost/shared_ptr.hpp> 
#include <boost/python.hpp> 
#include <boost/weak_ptr.hpp> 
#include <boost/enable_shared_from_this.hpp> 

namespace bp = boost::python; 

struct Empty: boost::enable_shared_from_this<Empty>{ }; 

struct Store 
{ 
    boost::weak_ptr<Empty> value; 

    void setValue(boost::shared_ptr<Empty> const& v) { 
     value = boost::weak_ptr<Empty>(v->shared_from_this()); 
     boost::shared_ptr<Empty> v_ok = value.lock(); 
     if (v_ok) { 
      std::cout << "ok, v has been stored" << std::endl; 
     } 
    } 

    boost::shared_ptr<Empty> getValue() { 
     boost::shared_ptr<Empty> p = value.lock(); 
     if (p) { 
      std::cout << "stored value : " << p << std::endl; 
     } else { 
      std::cout << "there's nothing here !" << std::endl; 
     } 
     return p; 
    } 
}; 

BOOST_PYTHON_MODULE (libmylibinterface) 
{ 
    bp::class_< Empty, boost::shared_ptr<Empty> >("Empty",bp::init<>()) 
     ; 

    bp::class_< Store, boost::shared_ptr<Store> >("Store") 
     .def("get",&Store::getValue) 
     .def("set",&Store::setValue); 

} 
+0

cảm ơn! Về một chủ đề liên quan, đây là lỗi liên quan đến con trỏ thông minh thứ hai mà tôi đã vấp phải trong một thời gian ngắn ([cái đầu tiên ở đây] (http://stackoverflow.com/questions/8203200/boostpython-and-seterase-weird-behaviour)) . Tôi có thể mong đợi std :: tr1 thực hiện để được tốt hơn? – girodt

+0

Tò mò, tôi tự hỏi nguyên nhân gốc rễ của điều này có liên quan không. Tôi nghĩ rằng tôi có thể hiểu lý do đằng sau so sánh số lượng tham chiếu - vì đó là danh tính của một tập hợp con trỏ được chia sẻ và trong một số trường hợp lạ, người dùng có thể có hai bộ chia sẻ khác nhau cho một đối tượng được chia sẻ, điều này có thể hữu ích nếu deleter cho ít nhất một trong hai bộ đã không thực sự xóa. Vẫn còn hơi khó hiểu dù ... – James

3

workaround khác là để vượt qua trong một tham chiếu đến shared_ptr, không phải là đối tượng shared_ptr riêng của mình. Trong trường hợp đó rõ ràng là boost::python-Wrapper không tạo riêng biệt này shared_ptr riêng biệt lạ.

Btw. Tôi bạn nhìn vào loại của lớp được trỏ đến bởi shared_ptr.pn.pi_ bạn sẽ thấy rằng "original" shared_ptr đưa ra cho Python chứa một con trỏ tới một đối tượng sp_counted_impl_p<POINTEE_TYPE> trong khi shared_ptr được trả về từ Python chứa một con trỏ tới a sp_counted_impl_pd<void*, shared_ptr_deleter> object (xem smart_ptr/detail/sp_counted_impl.hpp) Biến thể pd không chứa tham chiếu đến con trỏ. Tôi nghi ngờ rằng đối tượng sp_counted_impl_pd này bằng cách nào đó tham khảo đối tượng sp_counted_impl_p. Điều này sẽ giải thích tại sao hàm hủy không được gọi khi số tham chiếu của shared_ptr được trả về từ Python giảm xuống 0. Thực tế là weak_ptr không hoạt động trong trường hợp này có thể chỉ đơn giản là một lỗi sau đó ..?

Các vấn đề liên quan