wrapping a list of structs with boost.python

前端 未结 2 464
离开以前
离开以前 2021-01-03 04:09

I have a C++ function that returns a list of structs. Inside the struct, there are more lists of structs.

struct CameraInfo {
    CamName                              


        
相关标签:
2条回答
  • 2021-01-03 04:50

    Does it have to be std::list ? If you use std::vector instead you can use boost::python::vector_indexing_suite to wrap the list. See this post for details.

    If you must use std::list you'll need to create a helper class that wraps the std::list functionality with python's list methods. That can be quite involved, but doable.

    std_item.hpp:

    #include <list>
    #include <algorithm>
    #include <boost/python.hpp>
    
    template<class T>
    struct listwrap
    {
        typedef typename T::value_type value_type;
        typedef typename T::iterator iter_type;
    
        static void add(T & x, value_type const& v)
        {
            x.push_back(v);
        }
    
        static bool in(T const& x, value_type const& v)
        {
            return std::find(x.begin(), x.end(), v) != x.end();
        }
    
        static int index(T const& x, value_type const& v)
        {
            int i = 0;
            for(T::const_iterator it=x.begin(); it!=x.end(); ++it,++i)
                if( *it == v ) return i;
    
            PyErr_SetString(PyExc_ValueError, "Value not in the list");
            throw boost::python::error_already_set();
        }
    
        static void del(T& x, int i)
        {
            if( i<0 ) 
                i += x.size();
    
            iter_type it = x.begin();
            for (int pos = 0; pos < i; ++pos)
                ++it;
    
            if( i >= 0 && i < (int)x.size() ) {
                x.erase(it);
            } else {
                PyErr_SetString(PyExc_IndexError, "Index out of range");
                boost::python::throw_error_already_set();
            }
        }
    
        static value_type& get(T& x, int i)
        {
            if( i < 0 ) 
                i += x.size();
    
            if( i >= 0 && i < (int)x.size() ) {
                iter_type it = x.begin(); 
                for(int pos = 0; pos < i; ++pos)
                    ++it;
                return *it;                             
            } else {
                PyErr_SetString(PyExc_IndexError, "Index out of range");
                throw boost::python::error_already_set();
            }
        }
    
        static void set(T& x, int i, value_type const& v)
        {
            if( i < 0 ) 
                i += x.size();
    
            if( i >= 0 && i < (int)x.size() ) {
                iter_type it = x.begin(); 
                for(int pos = 0; pos < i; ++pos)
                    ++it;
                *it = v;
            } else {
                PyErr_SetString(PyExc_IndexError, "Index out of range");
                boost::python::throw_error_already_set();
            }
        }
    };
    
    
    template<class T>
    void export_STLList(const char* typeName)
    {
        using namespace boost::python;
    
        class_<std::list<T> >(typeName)
            .def("__len__", &std::list<T>::size)
            .def("clear", &std::list<T>::clear)
            .def("append", &listwrap<T>::add,
                with_custodian_and_ward<1,2>()) // to let container keep value
            .def("__getitem__", &listwrap<T>::get,
                return_value_policy<copy_non_const_reference>())
            .def("__setitem__", &listwrap<T>::set,
                with_custodian_and_ward<1,2>()) // to let container keep value
            .def("__delitem__", &listwrap<T>::del)
            .def("__contains__", &listwrap<T>::in)
            .def("__iter__", iterator<std::list<T> >())
            .def("index", &listwrap<T>::index);
    }
    

    usage:

    typedef std::list<int> intlist;
    export_STLList<int>("intlist");
    
    0 讨论(0)
  • 2021-01-03 04:57

    If one-way (from c++ to python) wrapping is enough, then you can define a direct converter from list<list<YourClass> > -- see my vector<vector<string> > converter -- just change types as you need, and don't forget to register the converter.

    You could also have a method returning python::list (which would itself contain python::list's with your objects) which would iterate over c++ nested list and build native python list out of it, but it would only work in one case you have.

    For two-way conversions, either have a look at my file (which contains both-way converters for different types) -- advantage being you get native python lists, disadvatage is copying the objects. For two-way conversion of large collections, indexing_suite is definitely the way to go.

    There is indexing_suite_v2, which is allegedly much better, including direct support for std::list and std::map, though unfortunately quite badly documented (last time I looked, about 1.5 years ago) and not official part of boost::python.

    0 讨论(0)
提交回复
热议问题