does boost python support a function returning a v

2019-02-11 08:10发布

问题:

I am new to python, I have looked at boost python, and it looks very impressive. However going through the introduction I can not find any examples where, vector of objects are returned as python list/tuples.

i.e Take this example, I want to expose class X, Cont and all its functions. critical bit being return a vector of X's or strings to python

  class X {};

   class Cont {

       .....
       // how can this be exposed using boost python
       const std::vector<X>&  const_ref_x_vec() const { return x_vec_;}
       std::vector<X> value_x_vec() const { return x_vec;}

       const std::vector<std::string>& const_ref_str_vec() const { return str_vec_;}
       std::vector<std::string> value_str_vec() const { return str_vec_; }

       ...
   private:
       std::vector<X> x_vec_;
       std::vector<std::string> str_vec_;
  };

My own fruitless attempt at trying to expose the functions like const_ref_x_vec(), value_x_vec(),etc just leads to compile errors.

from googling around I have not seen any example that support returning vectors by value or reference. Is this even possible with boost python? are there any workarounds ? should I be using SWIG for this case ?

Any help appreciated.

Avtar

回答1:

Autopulated's reason was essentially correct, but the code was more complicated then necessary.

The vector_indexing_suite can do all that work for you:

class_< std::vector<X> >("VectorOfX")
    .def(vector_indexing_suite< std::vector<X> >() )
    ;

There is a map_indexing_suite as well.



回答2:

Because you can't expose template types to python you have to explicitly expose each sort of vector that you want to use, for example this is from my code:

Generic template to wrap things:

namespace bp = boost::python;

inline void IndexError(){
    PyErr_SetString(PyExc_IndexError, "Index out of range");
    bp::throw_error_already_set();
}

template<class T>
struct vec_item{
    typedef typename T::value_type V;
    static V& get(T& x, int i){
        static V nothing;
        if(i < 0) i += x.size();
        if(i >= 0 && i < int(x.size())) return x[i];
        IndexError();
        return nothing;
    }
    static void set(T& x, int i, V const& v){
        if(i < 0) i += x.size();
        if(i >= 0 && i < int(x.size())) x[i] = v;
        else IndexError();
    }
    static void del(T& x, int i){
        if(i < 0) i += x.size();
        if(i >= 0 && i < int(x.size())) x.erase(x.begin() + i);
        else IndexError();
    }
    static void add(T& x, V const& v){
        x.push_back(v);
    }
};

Then, for each container:

    // STL Vectors:
    // LineVec
    bp::class_< std::vector< Line > >("LineVec")
        .def("__len__", &std::vector< Line >::size)
        .def("clear", &std::vector< Line >::clear)
        .def("append", &vec_item< std::vector< Line > >::add, 
              bp::with_custodian_and_ward<1, 2>()) // let container keep value
        .def("__getitem__", &vec_item< std::vector< Line > >::get,
             bp::return_value_policy<bp::copy_non_const_reference>())
        .def("__setitem__", &vec_item< std::vector< Line > >::set,
             bp::with_custodian_and_ward<1,2>()) // to let container keep value
        .def("__delitem__", &vec_item< std::vector< Line > >::del)
        .def("__iter__", bp::iterator< std::vector< Line > >())
    ;
    // ... 

A similar approach is possible for std::map. I used lots of help from wiki.python.org when writing this.