2014-10-05 27 views
0

假設有一個類,它實現了一個SomeType的數組。它用C++編寫,並使用boost :: python封裝到Python中。如何包裝函數重載的類型?

BOOST_PYTHON_MODULE(my_array_module) 
{ 
    class_<MyArray>("MyArray") 
    // a few '.def's ... 
    .def("__getitem__", ???) 
    ; 
} 

在Python的__getitem__函數可以採取一個索引並返回SomeType值,或採取一個切片對象,並返回切片。

There是如何處理在C++中重載的函數以將它們包裝到不同的Python函數中。 There是如何在Python中創建一個重載函數,如果重載意味着不同數目的參數。

但如何包裝重載功能,如果他們不同的arument類型?我需要在C++中使用2 getitem函數。

const SomeType& getitem(PyObject *self, size_t pos) { 
    // ... 
} 

MyArray getitem(PyObject *self, slice sl) { 
    // ... 
} 

如果您嘗試使用描述hereBOOST_PYTHON_FUNCTION_OVERLOADS方式來包裝它,它不會編譯。

我可以做一個函數

PyObject* getitem(PyObject *self, PyObject *pos_or_slice) { 
    extract<size_t> get_pos(pos_or_slice); 
    if (get_pos.check()) { 
     // return a single item (SomeType) 
    } 
    else { 
     // return a slice (MyArray) 
    } 
} 

,但我不知道如何正確地包裝成 PyObject*,這樣,這將是與class_產生的包裝一致。

+1

只是直接傳遞函數,[這裏是使用boost :: python](https://gist.github.com/zed/)使用不同的C++函數(使用不同參數類型)實現的相同Python函數的示例353005#文件升壓蟒蛇-XOR-CPP-L99) – jfs 2014-10-06 02:39:39

回答

1

簡而言之,只要C++函數具有不同的參數類型,那麼每個函數都可以作爲相同的Python函數進行公開,並且可以單獨調用def()。 Boost.Python將根據類型轉換來處理調度。如果類型不明確,那麼通常需要創建並公開基於檢查參數手動處理分派的輔助功能。


在這裏是一個完整的最小示例演示經由任一個索引或切片訪問實體模型Counter類的數據:

#include <vector> 
#include <boost/range/algorithm.hpp> 
#include <boost/range/irange.hpp> 
#include <boost/python.hpp> 
#include <boost/python/slice.hpp> 

/// @brief Mockup class that creates a range from 0 to N. 
struct counter 
{ 
    counter(std::size_t n) 
    { 
    data.reserve(n); 
    boost::copy(boost::irange(std::size_t(0), n), std::back_inserter(data)); 
    } 

    std::vector<int> data; 
}; 

/// @brief Handle index access for counter object. 
int spam_getitem_index(const counter& self, int index) 
{ 
    // Attempt to convert to positive index. 
    if (index < 0) 
    { 
    index += self.data.size(); 
    } 

    // Check for valid range. 
    if (index < 0 || self.data.size() <= index) 
    { 
     throw std::out_of_range("counter index out of range"); 
    } 

    return self.data[index]; 
} 

/// @brief Handle slicing for counter object. 
boost::python::list spam_getitem_slice(
    const counter& self, 
    boost::python::slice slice) 
{ 
    namespace python = boost::python; 
    python::list result; 

    // Boost.Python will throw std::invalid_argument if the range would be 
    // empty. 
    python::slice::range<std::vector<int>::const_iterator> range; 
    try 
    { 
    range = slice.get_indices(self.data.begin(), self.data.end()); 
    } 
    catch (std::invalid_argument) 
    { 
    return result; 
    } 

    // Iterate over fully-closed range. 
    for (; range.start != range.stop; std::advance(range.start, range.step)) 
    { 
    result.append(*range.start); 
    } 
    result.append(*range.start); // Handle last item. 
    return result; 
} 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 
    python::class_<counter>("Counter", python::init<int>()) 
    .def("__getitem__", &spam_getitem_slice) 
    .def("__getitem__", &spam_getitem_index) 
    ; 
} 

交互式用法:

>>> from example import Counter 
>>> counter = Counter(5) 
>>> assert(counter[:] == [0,1,2,3,4]) 
>>> assert(counter[:-2] == [0,1,2]) 
>>> assert(counter[-2:] == [3,4]) 
>>> assert(counter[::2] == [0,2,4]) 
>>> assert(counter[1::2] == [1,3]) 
>>> assert(counter[100:] == []) 
>>> assert(counter[1] == 1) 
>>> assert(counter[-1] == 4) 
>>> counter[100] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
IndexError: counter index out of range 
>>> counter[-100] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
IndexError: counter index out of range 

注意如何當spam_getitem_index()拋出一個std::out_of_range異常,Boost.Python翻譯它相關的IndexError Python異常。

相關問題