2013-06-08 38 views
5

Tôi có một tập tin tiêu đề như:Expose một vector như một memoryview sử dụng SWIG

#include <vector> 

inline std::vector<uint8_t>& vec() { 
    static std::vector<uint8_t> v { 'a', 'b', 'c', 'd' }; 
    return v; 
} 

inline const std::vector<uint8_t>& cvec() { 
    return vec(); 
} 

tôi có thể wrap it in SWIG using std_vector.i and pyabc.i nhưng đó là khá hiệu quả (có một bước nhảy giữa C++ và mã Python cho mỗi truy cập) và cho rằng những có nghĩa đen chỉ là một loạt các byte tôi nên để có thể bọc chúng với Python's memoryview interface.

Làm cách nào để hiển thị std::vector<uint8_t> của tôi dưới dạng Python memoryview?

Trả lời

7

Việc hiển thị số điện thoại dưới dạng memoryview yêu cầu tạo số Py_buffer trước tiên. Trong Python 3.3+ có một chức năng trợ giúp thuận tiện, PyMemoryView_FromMemory mà có rất nhiều công việc cho chúng tôi. Trong các phiên bản trước đó mặc dù chúng tôi sẽ cần phải thực hiện một vài bước mở rộng, vì vậy typemap cơ bản ra chúng tôi trông giống như:

%typemap(out) std::vector<uint8_t>&, const std::vector<uint8_t>& { 
    Py_buffer *buf=(Py_buffer*)malloc(sizeof *buf); 
    const bool ro = info<$1_type>::is_readonly(); 
    if (PyBuffer_FillInfo(buf, NULL, &((*$1)[0]), (*$1).size(), ro, PyBUF_ND)) { 
    // error, handle 
    } 
    $result = PyMemoryView_FromBuffer(buf); 
} 

Ở đây chúng ta đang cơ bản phân bổ một số bộ nhớ cho các Py_buffer. Điều này chỉ chứa các chi tiết của bộ đệm bên trong cho Python. Bộ nhớ mà chúng tôi phân bổ sẽ thuộc sở hữu của đối tượng memoryview sau khi được tạo. Thật không may kể từ khi nó sẽ được phát hành với một cuộc gọi đến free() chúng ta cần phải phân bổ nó với malloc(), mặc dù nó là mã C + +.

Bên cạnh Py_buffer và một tùy chọn Py_ObjectPyBuffer_FillInfo mất một void* (bộ đệm riêng của mình), kích thước của bộ đệm, một boolean chỉ ra nếu nó có khả năng ghi và một lá cờ. Trong trường hợp này lá cờ của chúng tôi chỉ đơn giản chỉ ra rằng chúng tôi đã cung cấp bộ nhớ tiếp giáp kiểu C cho bộ đệm.

Để quyết định xem nó có chỉ đọc hay không, chúng tôi đã sử dụng biến được xây dựng trong biến số $n_type của SWIG và một trình trợ giúp (có thể là đặc điểm kiểu C++ 11 nếu chúng ta muốn).

Để hoàn thành giao diện SWIG chúng ta cần cung cấp helper đó và bao gồm các tập tin tiêu đề, vì vậy toàn bộ điều trở thành:

%module test 

%{ 
#include "test.hh" 

namespace { 
    template <typename T> 
    struct info { 
    static bool is_readonly() { 
     return false; 
    } 
    }; 

    template <typename T> 
    struct info<const T&> { 
    static bool is_readonly() { 
     return true; 
    } 
    }; 
} 
%} 

%typemap(out) std::vector<uint8_t>&, const std::vector<uint8_t>& { 
    Py_buffer *buf=(Py_buffer*)malloc(sizeof *buf); 
    const bool ro = info<$1_type>::is_readonly(); 
    if (PyBuffer_FillInfo(buf, NULL, &((*$1)[0]), (*$1).size(), ro, PyBUF_ND)) { 
    // error, handle 
    } 
    $result = PyMemoryView_FromBuffer(buf); 
} 

%include "test.hh" 

Sau đó chúng tôi có thể thử nghiệm nó với:

import test 

print test.vec() 
print len(test.vec()) 
print test.vec()[0] 
print test.vec().readonly 
test.vec()[0]='z' 
print test.vec()[0] 

print "This should fail:" 
test.cvec()[0] = 0 

nào làm việc như mong đợi, được thử nghiệm bằng Python 2.7.

So với chỉ gói nó bằng cách sử dụng std_vector.i phương pháp này không có một số hạn chế. Lớn nhất là chúng tôi không thể thay đổi kích cỡ véc tơ, hoặc chuyển đổi nó trở lại một vector sau này tầm thường. Chúng ta có thể làm việc xung quanh điều đó, ít nhất một phần bằng cách tạo một proxy SWIG cho vectơ như bình thường và sử dụng tham số thứ hai của PyBuffer_FillInfo để lưu trữ nó trong nội bộ. (Điều này cũng sẽ cần thiết nếu chúng ta phải quản lý quyền sở hữu của vectơ chẳng hạn).

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