2013-07-26 6 views
7

के माध्यम से एक सी-स्टाइल सरणी डेटा सदस्य पायथन पर एक्सपोज़ करना मेरे पास struct है जिसमें सी-स्टाइल सरणी डेटा सदस्य है। मैं इस संरचना को पायथन के संपर्क में रखना चाहता हूं, और यह डेटा सदस्य पायथन में एक सूची के रूप में सुलभ हो सकता है।Boost.Python

struct S 
{ 
    char arr[4128]; 
}; 

void foo(S const *) 
{} 

BOOST_PYTHON_MODULE(test) 
{ 
    using namespace boost::python; 

    class_<S>("S") 
    .def_readwrite("arr", &S::arr) 
    ; 

    def("foo", foo); 
} 

कोड ऊपर का निर्माण करने के

error C2440: '=' : cannot convert from 'const char [4128]' to 'char [4128]' 

सी शैली सरणियों आबंटित नहीं हैं विफल रहता है, तो त्रुटि समझ में आता है। कोड संकलित करता है अगर मैं किसी सरणी के बजाय डेटा सदस्य को सादा char में बदलता हूं।

मैं सरणी को std::array या किसी अन्य कंटेनर से प्रतिस्थापित नहीं कर सकता क्योंकि संरचना सी सी एपीआई द्वारा खपत की जा रही है। एकमात्र समाधान मैं के बारे में सोच सकते हैं रैपर के एक जोड़े को लिखने और निम्नलिखित

  1. एक struct S1 डुप्लिकेट S यह सिवाय एक सी शैली सरणी
  2. एक foo_wrapper कि स्वीकार करने के बजाए एक std::array होगा क्या करना है एक S1 const *, प्रतियां एक S उदाहरण के लिए खत्म हो सामग्री और foo
  3. कॉल एक to_python_converter रजिस्टर एक अजगर सूची
को std::array कन्वर्ट करने के लिए

यह काम करना चाहिए, और मैं इस बिंदु पर डेटा कॉपी करने के बारे में बहुत चिंतित नहीं हूं, लेकिन यह अच्छा होगा अगर मैं इससे बच सकूं और इन सभी हुप्स के माध्यम से सीधे बिना सरणी का पर्दाफाश कर सकूं।

तो सवाल यह है कि, मैं उस सी-स्टाइल सरणी डेटा सदस्य को पायथन में सूची के रूप में कैसे बेनकाब कर सकता हूं?

उत्तर

4

समाधान मैं आ पर एक वर्ग array_ref एक सी शैली सरणी में एक गैर मालिक दृश्य प्रदान करता है कि बनाने के लिए किया गया था। एक और वर्ग, array_indexing_suite, boost::python::vector_indexing_suite से कॉपी-पेस्ट किया गया, सभी सदस्य फ़ंक्शंस जो मूल से फेंकने वाली सरणी में संशोधित सरणी के आकार को म्यूट करते हैं, तब array_ref को पाइथन से अनुक्रमण संचालन की अनुमति देने के लिए उपयोग किया जाता था।प्रासंगिक कोड के नीचे है:

array_ref

#include <cstddef> 
#include <iterator> 
#include <stdexcept> 

/** 
* array_ref offers a non-const view into an array. The storage for the array is not owned by the 
* array_ref object, and it is the client's responsibility to ensure the backing store reamins 
* alive while the array_ref object is in use. 
* 
* @tparam T 
*  Type of elements in the array 
*/ 
template<typename T> 
class array_ref 
{ 
public: 
    /** Alias for the type of elements in the array */ 
    typedef T value_type; 
    /** Alias for a pointer to value_type */ 
    typedef T *pointer; 
    /** Alias for a constant pointer to value_type */ 
    typedef T const *const_pointer; 
    /** Alias for a reference to value_type */ 
    typedef T& reference; 
    /** Alias for a constant reference to value_type */ 
    typedef T const& const_reference; 
    /** Alias for an iterator pointing at value_type objects */ 
    typedef T *iterator; 
    /** Alias for a constant iterator pointing at value_type objects */ 
    typedef T const *const_iterator; 
    /** Alias for a reverse iterator pointing at value_type objects */ 
    typedef std::reverse_iterator<iterator> reverse_iterator; 
    /** Alias for a constant reverse iterator pointing at value_type objects */ 
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator; 
    /** Alias for an unsigned integral type used to represent size related values */ 
    typedef std::size_t size_type; 
    /** Alias for a signed integral type used to represent result of difference computations */ 
    typedef std::ptrdiff_t difference_type; 

    /** Default constructor */ 
    constexpr array_ref() noexcept = default; 

    /** 
    * Constructor that accepts a pointer to an array and the number of elements pointed at 
    * 
    * @param arr 
    * Pointer to array 
    * @param length 
    * Number of elements pointed at 
    */ 
    constexpr array_ref(pointer arr, size_type length) noexcept 
    : begin_(arr) 
    , length_(length) 
    {} 

    /** 
    * Constructor that accepts a reference to an array 
    * 
    * @tparam N 
    * Number of elements in the array 
    */ 
    template<size_type N> 
    constexpr array_ref(T (&arr)[N]) noexcept 
    : begin_(&arr[0]) 
    , length_(N) 
    {} 

    /** 
    * Constructor taking a pair of pointers pointing to the first element and one past the last 
    * element of the array, respectively. 
    * 
    * @param first 
    * Pointer to the first element of the array 
    * @param last 
    * Pointer to one past the last element of the array 
    */ 
    array_ref(pointer first, pointer last) noexcept 
    : begin_(first) 
    , length_(static_cast<size_type>(std::distance(first, last))) 
    {} 

    /** Copy constructor */ 
    constexpr array_ref(array_ref const&) noexcept = default; 

    /** Copy assignment operator */ 
    array_ref& operator=(array_ref const&) noexcept = default; 

    /** Move constructor */ 
    constexpr array_ref(array_ref&&) noexcept = default; 

    /** Move assignment operator */ 
    array_ref& operator=(array_ref&&) noexcept = default; 

    /** 
    * Returns an iterator to the first element of the array. If the array is empty, the 
    * returned iterator will be equal to end(). 
    * 
    * @return An iterator to the first element of the array 
    */ 
    /*constexpr*/ iterator begin() noexcept 
    { 
    return begin_; 
    } 

    /** 
    * Returns a constant iterator to the first element of the array. If the array is empty, the 
    * returned iterator will be equal to end(). 
    * 
    * @return A constant iterator to the first element of the array 
    */ 
    constexpr const_iterator begin() const noexcept 
    { 
    return begin_; 
    } 

    /** 
    * Returns a constant iterator to the first element of the array. If the array is empty, the 
    * returned iterator will be equal to end(). 
    * 
    * @return A constant iterator to the first element of the array 
    */ 
    constexpr const_iterator cbegin() const noexcept 
    { 
    return begin_; 
    } 

    /** 
    * Returns an iterator to the element following the last element of the array. 
    * 
    * @return An iterator to the element following the last element of the array 
    */ 
    /*constexpr*/ iterator end() noexcept 
    { 
    return begin() + size(); 
    } 

    /** 
    * Returns a constant iterator to the element following the last element of the array. 
    * 
    * @return A constant iterator to the element following the last element of the array 
    */ 
    constexpr const_iterator end() const noexcept 
    { 
    return begin() + size(); 
    } 

    /** 
    * Returns a constant iterator to the element following the last element of the array. 
    * 
    * @return A constant iterator to the element following the last element of the array 
    */ 
    constexpr const_iterator cend() const noexcept 
    { 
    return cbegin() + size(); 
    } 

    /** 
    * Returns a reverse iterator to the first element of the reversed array. It corresponds to the 
    * last element of the non-reversed array. 
    * 
    * @return A reverse iterator to the first element of the reversed array 
    */ 
    reverse_iterator rbegin() noexcept 
    { 
    return reverse_iterator(end()); 
    } 

    /** 
    * Returns a constant reverse iterator to the first element of the reversed array. It corresponds 
    * to the last element of the non-reversed array. 
    * 
    * @return A constant reverse iterator to the first element of the reversed array 
    */ 
    const_reverse_iterator rbegin() const noexcept 
    { 
    return const_reverse_iterator(cend()); 
    } 

    /** 
    * Returns a constant reverse iterator to the first element of the reversed array. It corresponds 
    * to the last element of the non-reversed array. 
    * 
    * @return A constant reverse iterator to the first element of the reversed array 
    */ 
    const_reverse_iterator crbegin() const noexcept 
    { 
    return const_reverse_iterator(cend()); 
    } 

    /** 
    * Returns a reverse iterator to the element following the last element of the reversed array. It 
    * corresponds to the element preceding the first element of the non-reversed array. 
    * 
    * @return A reverse iterator to the element following the last element of the reversed array 
    */ 
    reverse_iterator rend() noexcept 
    { 
    return reverse_iterator(begin()); 
    } 

    /** 
    * Returns a constant reverse iterator to the element following the last element of the reversed 
    * array. It corresponds to the element preceding the first element of the non-reversed array. 
    * 
    * @return A constant reverse iterator to the element following the last element of the reversed 
    *   array 
    */ 
    const_reverse_iterator rend() const noexcept 
    { 
    return const_reverse_iterator(cbegin()); 
    } 

    /** 
    * Returns a constant reverse iterator to the element following the last element of the reversed 
    * array. It corresponds to the element preceding the first element of the non-reversed array. 
    * 
    * @return A constant reverse iterator to the element following the last element of the reversed 
    *   array 
    */ 
    const_reverse_iterator crend() const noexcept 
    { 
    return const_reverse_iterator(cbegin()); 
    } 

    /** 
    * Returns the number of elements in the array. 
    * 
    * @return The number of elements in the array 
    */ 
    constexpr size_type size() const noexcept 
    { 
    return length_; 
    } 

    /** 
    * Indicates whether the array has no elements 
    * 
    * @return true if the array has no elements, false otherwise 
    */ 
    constexpr bool empty() const noexcept 
    { 
    return size() == 0; 
    } 

    /** 
    * Returns a reference to the element at the specified location. 
    * 
    * @return A reference to the element at the specified location 
    * @pre i < size() 
    */ 
    /*constexpr*/ reference operator[](size_type i) 
    { 
#ifndef NDEBUG 
    return at(i); 
#else 
    return *(begin() + i); 
#endif 
    } 

    /** 
    * Returns a constant reference to the element at the specified location. 
    * 
    * @return A constant reference to the element at the specified location 
    * @pre i < size() 
    */ 
    constexpr const_reference operator[](size_type i) const 
    { 
#ifndef NDEBUG 
    return at(i); 
#else 
    return *(begin() + i); 
#endif 
    } 

    /** 
    * Returns a reference to the element at the specified location, with bounds checking. 
    * 
    * @return A reference to the element at the specified location 
    * @throw std::out_of_range if the specified index is not within the range of the array 
    */ 
    /*constexpr*/ reference at(size_type i) 
    { 
    if(i >= size()) { 
     throw std::out_of_range("index out of range"); 
    } 
    return *(begin() + i); 
    } 

    /** 
    * Returns a constant reference to the element at the specified location, with bounds checking. 
    * 
    * @return A constant reference to the element at the specified location 
    * @throw std::out_of_range if the specified index is not within the range of the array 
    */ 
    /*constexpr*/ const_reference at(size_type i) const 
    { 
    if(i >= size()) { 
     throw std::out_of_range("index out of range"); 
    } 
    return *(begin() + i); 
    } 

    /** 
    * Returns a reference to the first element of the array 
    * 
    * @return A reference to the first element of the array 
    * @pre empty() == false 
    */ 
    /*constexpr*/ reference front() noexcept 
    { 
    return *begin(); 
    } 

    /** 
    * Returns a reference to the first element of the array 
    * 
    * @return A reference to the first element of the array 
    * @pre empty() == false 
    */ 
    constexpr const_reference front() const noexcept 
    { 
    return *begin(); 
    } 

    /** 
    * Returns a reference to the last element of the array 
    * 
    * @return A reference to the last element of the array 
    * @pre empty() == false 
    */ 
    /*constexpr*/ reference back() noexcept 
    { 
    return *(end() - 1); 
    } 

    /** 
    * Returns a constant reference to the last element of the array 
    * 
    * @return A constant reference to the last element of the array 
    * @pre empty() == false 
    */ 
    constexpr const_reference back() const noexcept 
    { 
    return *(end() - 1); 
    } 

    /** 
    * Returns a pointer to the address of the first element of the array 
    * 
    * @return A pointer to the address of the first element of the array 
    */ 
    /*constexpr*/ pointer data() noexcept 
    { 
    return begin(); 
    } 

    /** 
    * Returns a constant pointer to the address of the first element of the array 
    * 
    * @return A constant pointer to the address of the first element of the array 
    */ 
    constexpr const_pointer data() const noexcept 
    { 
    return begin(); 
    } 

    /** 
    * Resets the operand back to its default constructed state 
    * 
    * @post empty() == true 
    */ 
    void clear() noexcept 
    { 
    begin_ = nullptr; 
    length_ = 0; 
    } 

private: 
    /** Pointer to the first element of the referenced array */ 
    pointer begin_ = nullptr; 
    /** Number of elements in the referenced array */ 
    size_type length_ = size_type(); 
}; 

array_indexing_suite

#include <boost/python.hpp> 
#include <boost/python/suite/indexing/indexing_suite.hpp> 

#include <algorithm> 
#include <cstddef> 
#include <iterator> 
#include <type_traits> 

// Forward declaration 
template< 
    typename Array, 
    bool NoProxy, 
    typename DerivedPolicies> 
class array_indexing_suite; 


namespace detail { 

template<typename Array, bool NoProxy> 
struct final_array_derived_policies 
: array_indexing_suite<Array, NoProxy, final_array_derived_policies<Array, NoProxy>> 
{}; 

} /* namespace detail */ 


template< 
    typename Array, 
    bool NoProxy = std::is_arithmetic<typename Array::value_type>::value, 
    typename DerivedPolicies = detail::final_array_derived_policies<Array, NoProxy> 
> 
class array_indexing_suite 
    : public boost::python::indexing_suite<Array, 
             DerivedPolicies, 
             NoProxy> 
{ 
public: 
    typedef typename Array::value_type data_type; 
    typedef typename Array::value_type key_type; 
    typedef typename Array::size_type index_type; 
    typedef typename Array::size_type size_type; 
    typedef typename Array::difference_type difference_type; 

    static data_type& get_item(Array& arr, index_type i) 
    { 
    return arr[i]; 
    } 

    static void set_item(Array& arr, index_type i, data_type const& v) 
    { 
     arr[i] = v; 
    } 

    static void delete_item(Array& /*arr*/, index_type /*i*/) 
    { 
    ::PyErr_SetString(::PyExc_TypeError, "Cannot delete array item"); 
    boost::python::throw_error_already_set(); 
    } 

    static size_type size(Array& arr) 
    { 
    return arr.size(); 
    } 

    static bool contains(Array& arr, key_type const& key) 
    { 
    return std::find(arr.cbegin(), arr.cend(), key) != arr.cend(); 
    } 

    static index_type get_min_index(Array&) 
    { 
    return 0; 
    } 

    static index_type get_max_index(Array& arr) 
    { 
    return arr.size(); 
    } 

    static bool compare_index(Array&, index_type a, index_type b) 
    { 
    return a < b; 
    } 

    static index_type convert_index(Array& arr, PyObject *i_) 
    { 
    boost::python::extract<long> i(i_); 
    if(i.check()) { 
     long index = i(); 

     if(index < 0) { 
     index += static_cast<decltype(index)>(DerivedPolicies::size(arr)); 
     } 

     if((index >= long(arr.size())) || (index < 0)) { 
     ::PyErr_SetString(::PyExc_IndexError, "Index out of range"); 
     boost::python::throw_error_already_set(); 
     } 
     return index; 
    } 

    ::PyErr_SetString(::PyExc_TypeError, "Invalid index type"); 
    boost::python::throw_error_already_set(); 
    return index_type(); 
    } 

    static boost::python::object get_slice(Array& arr, index_type from, index_type to) 
    { 
     if(from > to) { 
     return boost::python::object(Array()); 
     } 
     return boost::python::object(Array(arr.begin() + from, arr.begin() + to)); 
    } 

    static void set_slice(Array& arr, index_type from, index_type to, data_type const& v) 
    { 
    if(from > to) { 
     return; 

    } else if(to > arr.size()) { 
     ::PyErr_SetString(::PyExc_IndexError, "Index out of range"); 
     boost::python::throw_error_already_set(); 

    } else { 
     std::fill(arr.begin() + from, arr.begin() + to, v); 

    } 
    } 

    template<typename Iter> 
    static void set_slice(Array& arr, index_type from, index_type to, Iter first, Iter last) 
    { 
    auto num_items = std::distance(first, last); 

    if((from + num_items) > arr.size()) { 
     ::PyErr_SetString(::PyExc_IndexError, "Index out of range"); 
     boost::python::throw_error_already_set(); 
     return; 
    } 

    if(from > to) { 
     std::copy(first, last, arr.begin() + from); 

    } else { 
     if(static_cast<decltype(num_items)>(to - from) != num_items) { 
     ::PyErr_SetString(::PyExc_TypeError, "Array length is immutable"); 
     boost::python::throw_error_already_set(); 
     return; 

     } 

     std::copy(first, last, arr.begin() + from); 
    } 
    } 

    static void delete_slice(Array& /*arr*/, index_type /*from*/, index_type /*to*/) 
    { 
    ::PyErr_SetString(::PyExc_TypeError, "Cannot delete array item(s)"); 
    boost::python::throw_error_already_set(); 
    } 
}; 

अंत में, बिट्स बाइंडिंग बनाने के लिए आवश्यक।

struct foo 
{ 
    char data[100]; 
}; 

BOOST_PYTHON_MODULE(foo_module) 
{ 
    using namespace boost::python; 

    class_<array_ref<unsigned char>>("uchar_array") 
    .def(array_indexing_suite<array_ref<unsigned char>>()) 
    ; 

    class_<foo>("foo", "Foo's description") 
    .add_property("data", 
        /* getter that returns an array_ref view into the array */ 
        static_cast<array_ref<unsigned char>(*)(foo *)>(
         [](foo *obj) { 
         return array_ref<unsigned char>(obj->data); 
         }), 
        "Array of data bytes") 
    ; 
} 
+1

कृपया देखें कि क्या अपस्ट्रीम आपके सहायक स्निपेट –

+0

को बैरी के पोस्ट से भी नोटिस करेगा http://stackoverflow.com/questions/16845547/using-c11-lambda-as-accessor-function-in-boostpythons-add-property-get -जब आप उस भयानक लैम्ब्डा संपत्ति को लैम्ब्डा के सामने + के साथ कास्ट कर सकते हैं –

4

जैसा कि आपने देखा है, बूस्ट। पाइथन दुर्भाग्यवश सी सरणी के लिए स्वचालित कनवर्टर्स प्रदान नहीं करता है, और यहां तक ​​कि एसटीएल कंटेनर रैपर भी प्रदान करता है, यह नहीं है कि मैं इस पर पहुंचने की सिफारिश कैसे करूंगा (कम से कम यदि आपकी असली समस्या है आपके उदाहरण के समान एक सरणी कितनी बड़ी है, और यदि आप जानते हैं कि आप इसे एक वास्तविक पायथन टुपल के रूप में बेनकाब करना चाहते हैं)।

मैं एक फ़ंक्शन लिखने की अनुशंसा करता हूं जो सी पाइर को पाइथन सी-एपीआई में सीधे पाइथन सी-एपीआई या उसके बूस्ट :: पायथन रैपर का उपयोग करके परिवर्तित करता है, और उसके बाद डेटा सदस्य को संपत्ति के माध्यम से उजागर करता है। जबकि आप एक छोटे से सरणी के लिए, एक प्रयास के लायक नहीं है, एक tuple के बजाय एक NumPy सरणी का उपयोग कर डेटा प्रतिलिपि से बच सकते हैं।

उदाहरण के लिए:

namespace bp = boost::python; 

bp::tuple wrap_arr(S const * s) { 
    bp::list a; 
    for (int i = 0; i < 10; ++i) { 
     a.append(s->arr[i]); 
    } 
    return bp::tuple(a); 
} 

BOOST_PYTHON_MODULE(test) { 
    bp::class_<S>("S") 
    .add_property("arr", wrap_arr) 
    ; 
} 
+0

+1 मैं 'add_property' स्वीकृत गेटर्स और सेटर्स को भूल गया था। इसके अलावा, मुझे वास्तव में एक सूची की आवश्यकता है, न कि टुपल, क्योंकि मैं इसे अजगर की तरफ संशोधित करना चाहता हूं। आपका समाधान जो मैं सोच रहा था उससे बेहतर है, खासकर जब से मैं गेटटर और सेटर को भेड़ के बच्चे के रूप में लिख सकता हूं और यह संक्षिप्त हो सकता है। लेकिन सरणी वास्तव में 10 वर्ण नहीं है, यह 4128 है। मैं उस जानकारी के साथ प्रश्न भी अपडेट करूंगा; मुझे लोगों को यह विश्वास करने के लिए प्रेरित नहीं करना चाहिए था कि कॉपी बेहद तुच्छ है (भले ही मुझे प्रतिलिपि साबित करने का मौका नहीं मिला है मेरे लिए अभी भी निषिद्ध है) – Praetorian

+1

यदि आपको जगहों पर तत्वों को संशोधित करने में सक्षम होना चाहिए, लेकिन आप तत्वों को जोड़ने या हटाने में सक्षम होने की आवश्यकता नहीं है, तो आपको लगभग निश्चित रूप से इसे NumPy सरणी जैसे किसी चीज़ के माध्यम से बेनकाब करने की आवश्यकता है। यहां तक ​​कि एक पाइथन सूची आपको अपने डेटा की प्रतिलिपि बनाने के लिए मजबूर करेगी, इसलिए संशोधनों को सी ++ पर वापस प्रसारित नहीं किया जाएगा। तो या तो आप NumPy का उपयोग करते हैं, या आप एक सूची-जैसी प्रॉक्सी क्लास लिखते हैं और लपेटते हैं जो आपके सी सरणी अंडर-द-हूड को संशोधित करता है ... और यह मूल रूप से एक NumPy सरणी है। – jbosch

+0

इन-प्लेस संशोधन बिल्कुल वही है जो मैं करना चाहता हूं। क्या आपको न्यूमपी/बूस्ट के बारे में पता चल जाता है। पायथन उदाहरण मैं देख सकता था? – Praetorian

संबंधित मुद्दे