2014-12-29 13 views
5

Tôi có một lớp EventDispatcher triển khai mẫu đăng ký xuất bản. Giao diện của nó trông giống như thế này (đã được đơn giản hóa):Cách xử lý unique_ptr với SWIG

class EventDispatcher 
{ 
public: 
    void publish(const std::string& event_name, std::unique_ptr<Event> event); 

    std::unique_ptr<Subscription> subscribe(const std::string& event_name, std::unique_ptr<Callback> callback); 

private: 
    std::unordered_map<std::string, std::vector<std::unique_ptr<Callback>>> m_subscriptions; 
} 

Tôi muốn đưa lớp này vào Python. Tài liệu SWIG mới nhất nêu rõ rằng:

Không có xử lý con trỏ thông minh đặc biệt nào có sẵn cho tiêu chuẩn :: weak_ptr và std :: unique_ptr.

Tôi rất muốn ít nhất có thể tiếp tục sử dụng unique_ptr ở phía bên C++. Những lựa chọn của tôi là gì?

Tôi đã xem xét việc mở rộng lớp bằng cách sử dụng tính năng mở rộng của SWIG%, nhưng tôi không thể truy cập các thành viên riêng tư (m_subscriptions) bằng phương pháp này.

Tùy chọn duy nhất khác mà tôi có thể thấy là sử dụng bộ tiền xử lý SWIG để xác định các phương thức bổ sung, swig_publishswig_subscribe, nhưng điều này sẽ cắt tệp giao diện của tôi.

Trả lời

10

Có khá nhiều phạm vi để làm những việc hữu ích bằng cách sử dụng generic smart pointer support trong SWIG, bất chấp sự thiếu chú ý hỗ trợ trong C++ 11 ghi chú.

Tóm lại nếu có operator-> thì SWIG đã hợp nhất các thành viên của con trỏ vào con trỏ để cho phép chúng được sử dụng thay thế lẫn nhau trong ngôn ngữ đích trong một thời gian dài.

tôi đã đặt cùng một ví dụ đầy đủ về cách này có thể làm việc cho bạn, bằng cách sử dụng test.hh dụ tập tin Hader dưới đây:

#include <memory> 
#include <iostream> 

struct Foobar { 
    void baz() { std::cout << "This works\n"; } 
    int wibble; 
}; 

std::unique_ptr<Foobar> make_example() { 
    return std::unique_ptr<Foobar>(new Foobar); 
} 

void dump_example(const std::unique_ptr<Foobar>& in) { 
    std::cout << in->wibble << "\n"; 
    in->baz(); 
} 

Để sử dụng unique_ptr hợp lý bên trong Python tôi đã phải viết sau tập SWIG, std_unique_ptr.i:

namespace std { 
    %feature("novaluewrapper") unique_ptr; 
    template <typename Type> 
    struct unique_ptr { 
    typedef Type* pointer; 

    explicit unique_ptr(pointer Ptr); 
    unique_ptr (unique_ptr&& Right); 
    template<class Type2, Class Del2> unique_ptr(unique_ptr<Type2, Del2>&& Right); 
    unique_ptr(const unique_ptr& Right) = delete; 


    pointer operator->() const; 
    pointer release(); 
    void reset (pointer __p=pointer()); 
    void swap (unique_ptr &__u); 
    pointer get() const; 
    operator bool() const; 

    ~unique_ptr(); 
    }; 
} 

%define wrap_unique_ptr(Name, Type) 
    %template(Name) std::unique_ptr<Type>; 
    %newobject std::unique_ptr<Type>::release; 

    %typemap(out) std::unique_ptr<Type> %{ 
    $result = SWIG_NewPointerObj(new $1_ltype(std::move($1)), $&1_descriptor, SWIG_POINTER_OWN); 
    %} 

%enddef 

trong đó bao gồm đủ của một tập hợp con của định nghĩa của std::unique_ptr có ích. (Bạn có thể thêm hoặc loại bỏ các hàm tạo phụ thuộc vào chính xác ngữ nghĩa bạn muốn trong Python, tôi đã bỏ qua các tùy chỉnh ở đây).

Nó cũng thêm macro wrap_unique_ptr để thiết lập hỗ trợ. Sơ đồ trang web chỉ ép buộc mã được tạo ra của SWIG sử dụng hàm tạo di chuyển thay vì hàm tạo bản sao khi trả về theo giá trị.

Chúng ta có thể sử dụng nó theo cách sau:

%module test 

%{ 
#include "test.hh" 
%} 

%include "std_unique_ptr.i" 

wrap_unique_ptr(FooUniquePtr, Foobar); 

%include "test.hh" 

tôi đã xây dựng này với:

swig3.0 -py3 -c++ -python -Wall test.i 
g++ -Wall -Wextra -Wno-missing-field-initializers test_wrap.cxx -std=c++11 -I/usr/include/python3.4/ -lpython3.4m -shared -o _test.so 

nào cho phép chúng ta sử dụng Python sau:

from test import * 

a = make_example() 

print(a) 
a.wibble = 1234567 
a.baz() 

dump_example(a) 

a.baz() 

print(bool(a)) 
print(bool(FooUniquePtr(None))) 

b=a.release() 
print(b) 

ý rằng mặc dù là một số unique_ptr<Foobar>, chúng tôi vẫn có thể nói a.baz()a.wibble. Phương thức release() cũng trả về một con trỏ 'thô' có thể sử dụng, được sở hữu bởi Python ngay bây giờ (vì nếu không nó sẽ không có chủ sở hữu). get() trả về một con trỏ được mượn bên trong Python như bạn mong đợi.

Tùy thuộc vào cách bạn dự định sử dụng con trỏ, đây có thể là khởi đầu tốt cho các kiểu chữ của riêng bạn và sạch hơn so với %extendrelease() ở mọi nơi bạn có unique_ptrs.

So với %shared_ptr, điều này không sửa đổi trong các bản đồ và nó không thay đổi các nhà xây dựng theo cách tương tự như hỗ trợ shared_ptr. Đó là trách nhiệm của bạn để lựa chọn khi con trỏ thô trở thành unique_ptrs trong Python vẫn còn.

Tôi đã viết câu trả lời tương tự cho using std::weak_ptr with SWIG trong khi quay lại.

+0

Một trong những thứ có thể dễ dàng thêm vào để nâng cao điều này sẽ là một typemap cho 'Type *'/'Type &' có thể xử lý hoặc là một unique_ptr hoặc một con trỏ thực. – Flexo

+0

Thú vị. Tôi đồng ý rằng điều này là sạch hơn nhiều so với việc trộn lẫn các tệp giao diện với các hàm bổ sung để xử lý các chuyển đổi giữa các con trỏ của unique_ptr và raw. Nó cũng cho thấy ý định sở hữu rõ ràng. Cảm ơn bạn đã trả lời chi tiết. – Homar

+0

Lưu ý: Lớp của tôi có một 'std :: unique_ptr pImpl' riêng, trong trường hợp này tôi phải ** không ** bao gồm bất kỳ' wrap_unique_ptr (RealImplUniquePtr, RealImpl) '(có thể cung cấp lỗi về' kiểu không đầy đủ' từ ' default_delete'), chỉ cần bọc các loại có sẵn hoàn toàn từ API công khai. – unhammer

-2

Chưa có hỗ trợ nào cho unique_ptr. http://www.swig.org/Doc3.0/CPlusPlus11.html

Bạn cần phải sử dụng con trỏ thông minh như sau: http://www.swig.org/Doc3.0/Library.html#Library_std_shared_ptr

+0

Tôi thực sự không muốn sử dụng một loại con trỏ thông minh khác. Tôi quan tâm đến cách tôi có thể tiếp tục sử dụng unique_ptr ở phía bên C++ nhưng hiển thị các hàm khác nhau cho Python sử dụng con trỏ thô. – Homar

2

Bizarrely, có vẻ như nó có thể %ignore một chức năng, %extend lớp để xác định một thực hiện thay thế chức năng bỏ qua và cuối cùng gọi ban đầu bỏ qua chức năng từ bên trong việc thực hiện thay thế của chức năng đó. Ví dụ:

%ignore EventDispatcher::subscribe(const std::string&, std::unique_ptr<Callback>); 

%include "EventDispatcher.hpp" 

%extend suborbital::EventDispatcher 
{ 
    EventSubscription* EventDispatcher::subscribe(const std::string& event_name, PyObject* callback) 
    { 
     std::unique_ptr<Callback> callback_ptr(new Callback(callback)); 
     return $self->subscribe(event_name, std::move(callback_ptr)).release(); 
    } 
} 
+0

Bạn có thể thực hiện việc này trực tiếp mà không cần gọi% ignore, miễn là bạn không phơi bày (khai báo) trong nguyên mẫu hàm gốc trong tệp .i – n00shie

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