00001 #ifndef toast_python_container_hpp_INCLUDED
00002 #define toast_python_container_hpp_INCLUDED
00003
00004 #include <exception>
00005 #include <vector>
00006 #include <deque>
00007 #include <list>
00008 #include <set>
00009 #include <map>
00010 #include <memory>
00011 #include <bitset>
00012 #include <string>
00013 #include <boost/python.hpp>
00014 #include <boost/python/slice.hpp>
00015 #include <boost/python/make_constructor.hpp>
00016 #include <toast/typeinfo.hpp>
00017 #include <toast/scope_guard.hpp>
00018 #include <boost/mpl/bool.hpp>
00019
00020
00027 namespace toast { namespace python {
00028
00052 template <typename T>
00053 struct register_container
00054 {
00055 register_container() { boost::STATIC_ASSERTION_FAILURE<false>(); }
00056 };
00057
00062 namespace detail {
00063
00064 template <typename Container>
00065 struct implicit
00066 {
00067 typedef register_container<Container> reg;
00068 static void * convertible(PyObject *obj)
00069 {
00070 return reg::convertible(obj);
00071 }
00072
00073 typedef boost::python::converter::rvalue_from_python_stage1_data
00074 rvalue_data;
00075 typedef boost::python::converter::rvalue_from_python_storage<Container>
00076 rvalue_storage;
00077
00078 static void destruct(Container *c) { c->~Container(); }
00079
00080 static void
00081 construct( PyObject *obj, rvalue_data *data )
00082 {
00083 using namespace boost::python;
00084 void *storage = ((rvalue_storage*)data)->storage.bytes;
00085
00086 new (storage) Container;
00087 toast::scope_guard guard(boost::bind(&implicit<Container>::destruct,
00088 static_cast<Container *>(storage)
00089 ));
00090 reg::copyContents(extract<object>(obj),
00091 *static_cast<Container *>(storage));
00092
00093 data->convertible = storage;
00094 guard.dismiss();
00095 }
00096 };
00097
00098 template <typename T, typename MakePtr>
00099 struct refwrapper
00100 {
00101 refwrapper( T & ) { ::boost::STATIC_ASSERTION_FAILURE<false>(); }
00102 };
00103
00104 template <typename T>
00105 struct refwrapper<T, boost::mpl::bool_<true> >
00106 {
00107 boost::python::object obj;
00108 refwrapper( T &t ) : obj(boost::python::ptr(&t)) {}
00109 };
00110
00111 template <typename T>
00112 struct refwrapper<T, boost::mpl::bool_<false> >
00113 {
00114 boost::python::object obj;
00115 refwrapper( T &t ) : obj(t) {}
00116 };
00117
00118 template <typename T>
00119 struct can_take_reference
00120 : boost::mpl::and_<boost::is_class<T>,
00121 boost::mpl::not_<boost::is_same<T,
00122 std::string> > > {};
00123 template <typename T>
00124 boost::python::object getrefobj( T &val )
00125 {
00126 return refwrapper<T, typename can_take_reference<T>::type>(val).obj;
00127 }
00128
00129 template <typename Container>
00130 struct register_std_container
00131 {
00132 typedef register_container<Container> reg;
00133 typedef typename Container::value_type value_type;
00134 static void copyContents(boost::python::object o, Container &container )
00135 {
00136 using namespace boost::python;
00137 handle<PyObject> iter(PyObject_GetIter(o.ptr()));
00138 while(handle<PyObject> item =
00139 handle<PyObject>(allow_null(PyIter_Next(iter.get())))) {
00140 reg::add(container, extract<value_type>(item.get()) );
00141 }
00142 }
00143
00144 static Container * make( boost::python::object o )
00145 {
00146 using namespace boost::python;
00147 if( !converter::implicit<object, Container>::convertible(o.ptr()) ) {
00148 PyErr_SetObject(PyExc_TypeError, o.ptr());
00149 throw_error_already_set();
00150 }
00151 std::auto_ptr<Container> c(new Container);
00152 reg::copyContents(o, *c);
00153 return c.release();
00154 }
00155
00156 static boost::python::list list( Container &container )
00157 {
00158 boost::python::list l;
00159 for( typename Container::iterator iter = container.begin();
00160 iter != container.end(); ++iter ) {
00161 l.append( getrefobj(*iter) );
00162 }
00163 return l;
00164 }
00165
00166 register_std_container()
00167 {
00168 using namespace boost::python;
00169
00170 static bool registered = false;
00171 if( registered )
00172 return;
00173 class_.reset(new boost::python::class_<Container>(toast::type_id<Container>().name().c_str(), no_init) );
00174
00175 class_->def("__init__", make_constructor(®::make) )
00176 .def( "__len__", &Container::size )
00177 ;
00178
00179 implicitly_convertible<object, Container>();
00180 registered = true;
00181 }
00182
00183 protected:
00184 boost::shared_ptr<boost::python::class_<Container> > class_;
00185 };
00186
00187 template <typename Container>
00188 struct register_std_sequence : register_std_container<Container>
00189 {
00190 typedef register_std_sequence<Container> reg;
00191 typedef typename Container::value_type value_type;
00192
00193 static void * convertible(PyObject *obj)
00194 {
00195 return PySequence_Check(obj) ? obj : 0;
00196 }
00197
00198 static void add( Container &c , value_type value )
00199 {
00200 c.push_back(value);
00201 }
00202
00203 void def_iter(boost::mpl::bool_<true>)
00204 {
00205 this->class_->def("__iter__",
00206 boost::python::iterator<Container,
00207 boost::python::return_internal_reference<> >() );
00208 }
00209
00210 void def_iter(boost::mpl::bool_<false>)
00211 {
00212 this->class_->def( "__iter__",
00213 boost::python::iterator<Container>() ) ;
00214 }
00215
00216 register_std_sequence()
00217 {
00218 using namespace boost::python;
00219 if( this->class_ ) {
00220 this->class_->def( "list", ®::list )
00221 .def( "append", ®::add )
00222 ;
00223
00224 def_iter(typename can_take_reference<value_type>::type());
00225
00226 }
00227 }
00228 };
00229
00230 template <typename Container>
00231 struct register_std_ra_sequence : register_std_sequence<Container>
00232 {
00233 typedef typename Container::value_type value_type;
00234 typedef register_std_ra_sequence<Container> reg;
00235
00236 static typename boost::mpl::if_<
00237 can_take_reference<value_type>,
00238 value_type &, value_type>::type
00239 getitem( Container &container, int index )
00240 {
00241 if( index < 0 )
00242 index += container.size();
00243 return container.at(index);
00244 }
00245
00246 static boost::python::list
00247 getslice( Container &container, boost::python::slice &s )
00248 {
00249 boost::python::list l;
00250 int begin = 0;
00251 if( s.start() ) {
00252 begin = boost::python::extract<int>(s.start());
00253 if( begin < 0 )
00254 begin = container.size() + begin;
00255 if( begin < 0 )
00256 begin = 0;
00257 else if( static_cast<unsigned>(begin) >= container.size() )
00258 begin = container.size() - 1;
00259 }
00260 int incr = 1;
00261 if( s.step() ) {
00262 incr = boost::python::extract<int>(s.step());
00263 }
00264 int end = 0;
00265 if( incr > 0 )
00266 end = container.size();
00267 if( s.stop() ) {
00268 end = boost::python::extract<int>(s.stop());
00269 if( end < 0 )
00270 end = container.size() + end;
00271 if( end < 0 )
00272 end = 0;
00273 else if( static_cast<unsigned>(end) >= container.size() )
00274 end = container.size();
00275 }
00276 if( incr > 0 )
00277 for( int i = begin; i < end; i += incr)
00278 l.append(getrefobj(container[i]));
00279 else
00280 for( int i = begin; i > end; i += incr)
00281 l.append(getrefobj(container[i]));
00282 return l;
00283 }
00284
00285 static void setitem( Container &container, int index, value_type value )
00286 {
00287 if( index < 0 )
00288 index += container.size();
00289 if( index < 0 || static_cast<unsigned>(index) >= container.size() ) {
00290 PyErr_SetString(PyExc_IndexError, "assignment index out of range");
00291 ::boost::python::throw_error_already_set();
00292 }
00293 container[index] = value;
00294 }
00295
00296 static void delitem( Container &container, int index )
00297 {
00298 if( index < 0 )
00299 index += container.size();
00300 if( index < 0 || static_cast<unsigned>(index) >= container.size() ) {
00301 PyErr_SetString(PyExc_IndexError, "assignment index out of range");
00302 ::boost::python::throw_error_already_set();
00303 }
00304 typename Container::iterator iter = container.begin() + index;
00305 container.erase(iter);
00306 }
00307
00308 void def_getitem(boost::mpl::bool_<true>)
00309 {
00310 this->class_->def("__getitem__", ®::getitem,
00311 boost::python::return_internal_reference<>() );
00312 }
00313
00314 void def_getitem(boost::mpl::bool_<false>)
00315 {
00316 this->class_->def( "__getitem__", ®::getitem);
00317 }
00318
00319 register_std_ra_sequence()
00320 {
00321 if( this->class_ ) {
00322 def_getitem(typename can_take_reference<value_type>::type());
00323
00324 this->class_->def( "__getitem__",
00325 ®::getslice)
00326 .def( "__setitem__",
00327 ®::setitem)
00328 .def( "__delitem__",
00329 ®::delitem)
00330 ;
00331 }
00332 }
00333 };
00334
00335 }
00336
00337 template <typename KeyType, typename ValueType>
00338 struct register_container< std::map<KeyType, ValueType> >
00339 : detail::register_std_container< std::map<KeyType, ValueType> >
00340 {
00341 typedef std::map<KeyType, ValueType> container_type;
00342 typedef register_container<container_type> reg;
00343 static void * convertible(PyObject *obj)
00344 {
00345 return PyMapping_Check(obj) ? obj : 0;
00346 }
00347
00348 static void
00349 copyContents( boost::python::object o, container_type &container )
00350 {
00351 using namespace boost::python;
00352
00353
00354
00355 char items_[] = "items";
00356 handle<PyObject> items(PyObject_CallMethod(o.ptr(), items_, NULL));
00357
00358 handle<PyObject> iter(PyObject_GetIter(items.get()));
00359 while( handle<PyObject> pitem =
00360 handle<PyObject>(allow_null(PyIter_Next(iter.get()))) ) {
00361
00362 tuple item = extract<tuple>(pitem.get());
00363 container.insert(std::pair<KeyType,
00364 ValueType>(extract<KeyType>(item[0]),
00365 extract<ValueType>(item[1])));
00366 }
00367 }
00368
00369 static boost::python::list keys( container_type &container )
00370 {
00371 boost::python::list l;
00372 for( typename container_type::iterator iter = container.begin();
00373 iter != container.end(); ++iter ) {
00374 l.append( boost::python::object(iter->first) );
00375 }
00376 return l;
00377 }
00378
00379 static boost::python::list values( container_type &container )
00380 {
00381 boost::python::list l;
00382 for( typename container_type::iterator iter = container.begin();
00383 iter != container.end(); ++iter ) {
00384 l.append( detail::getrefobj(iter->second) );
00385 }
00386 return l;
00387 }
00388
00389 static bool has_key( container_type &container, KeyType key )
00390 {
00391 return container.find(key) != container.end();
00392 }
00393
00394 static boost::python::object
00395 get(container_type &container, KeyType key,
00396 boost::python::object def = boost::python::object() )
00397 {
00398 typename container_type::iterator iter = container.find( key );
00399 if( iter == container.end() )
00400 return def;
00401 return detail::getrefobj(iter->second);
00402 }
00403
00404 static boost::python::object
00405 getitem( container_type &container, KeyType key )
00406 {
00407 typename container_type::iterator iter = container.find(key);
00408 if( iter == container.end() ) {
00409 PyErr_SetObject( PyExc_KeyError, boost::python::object(key).ptr() );
00410 ::boost::python::throw_error_already_set();
00411 }
00412 return detail::getrefobj(iter->second);
00413 }
00414
00415 static void
00416 setitem( container_type &container, KeyType key, ValueType value )
00417 {
00418 container[key] = value;
00419 }
00420
00421 static void delitem( container_type &container, KeyType key )
00422 {
00423 typename container_type::iterator iter = container.find(key);
00424 if( iter == container.end() ) {
00425 PyErr_SetObject( PyExc_KeyError, boost::python::object(key).ptr() );
00426 ::boost::python::throw_error_already_set();
00427 }
00428 else
00429 container.erase(iter);
00430 }
00431
00432 static boost::python::list items( container_type &container )
00433 {
00434 using namespace boost::python;
00435 boost::python::list l;
00436 for( typename container_type::iterator iter = container.begin();
00437 iter != container.end(); ++iter ) {
00438 l.append(boost::python::make_tuple(object(iter->first),
00439 detail::getrefobj(iter->second) ) );
00440 }
00441 return l;
00442 }
00443
00444 BOOST_PYTHON_FUNCTION_OVERLOADS(map_get_overloads,
00445 register_container<container_type>::get,
00446 2, 3 );
00447
00448 register_container()
00449 {
00450 if( this->class_ ) {
00451 this->class_->def( "keys", ®::keys )
00452 .def( "values", ®::values )
00453 .def( "has_key", ®::has_key )
00454 .def( "__contains__", ®::has_key )
00455 .def( "__getitem__", ®::getitem )
00456 .def( "__setitem__", ®::setitem )
00457 .def( "__delitem__", ®::delitem )
00458 .def( "clear", &container_type::clear )
00459 .def( "items", ®::items )
00460 .def( "get", ®::get, map_get_overloads() )
00461 ;
00462 }
00463 }
00464 };
00465
00466 template <typename ContainedType>
00467 struct register_container< std::set<ContainedType> >
00468 : detail::register_std_container< std::set<ContainedType> >
00469 {
00470 typedef std::set<ContainedType> container_type;
00471 static void * convertible(PyObject *obj)
00472 {
00473 boost::python::handle<PyObject> o(PyObject_GetIter(obj));
00474 if( !o ) {
00475 PyErr_Clear();
00476 return 0;
00477 }
00478 return obj;
00479 }
00480
00481 static void add( container_type &container, ContainedType const &val )
00482 {
00483 container.insert(val);
00484 }
00485
00486 static bool
00487 contains( container_type &container, ContainedType const &val )
00488 {
00489 typename container_type::iterator iter = container.find(val);
00490 return iter != container.end();
00491 }
00492
00493 static void discard( container_type &container, ContainedType const &val )
00494 {
00495 container.erase(val);
00496 }
00497
00498 static void remove( container_type &container, ContainedType const &val )
00499 {
00500 typename container_type::iterator iter = container.find(val);
00501 if( iter != container.end() )
00502 container.erase(iter);
00503 else {
00504 PyErr_SetObject( PyExc_KeyError, boost::python::object(val).ptr() );
00505 ::boost::python::throw_error_already_set();
00506 }
00507 }
00508
00509 register_container()
00510 {
00511 if( this->class_ ) {
00512 this->class_->def( "__contains__",
00513 ®ister_container<container_type>::contains )
00514 .def( "__iter__",
00515 boost::python::iterator<container_type>() )
00516 .def( "add",
00517 ®ister_container<container_type>::add )
00518 .def( "clear", &container_type::clear )
00519 .def( "discard", ®ister_container<container_type>::discard )
00520 .def( "remove", ®ister_container<container_type>::remove )
00521 ;
00522
00523 }
00524 }
00525 };
00526
00527 template <typename ContainedType>
00528 struct register_container< std::vector<ContainedType> >
00529 : detail::register_std_ra_sequence<std::vector<ContainedType> > {};
00530
00531 template <typename ContainedType>
00532 struct register_container< std::deque<ContainedType> >
00533 : detail::register_std_ra_sequence<std::deque<ContainedType> > {};
00534
00535 template <typename ContainedType>
00536 struct register_container< std::list<ContainedType> >
00537 : detail::register_std_sequence<std::list<ContainedType> > {};
00538
00539 template <size_t bitsize>
00540 struct register_container< std::bitset<bitsize> >
00541 {
00542 typedef register_container< std::bitset<bitsize> > reg;
00543 typedef std::bitset<bitsize> container_type;
00544 static void * convertible(PyObject *obj)
00545 {
00546 return PySequence_Check(obj) ? obj : 0;
00547 }
00548
00549 static container_type * make( boost::python::object o )
00550 {
00551 using namespace boost::python;
00552 if(!converter::implicit<object, container_type>::convertible(o.ptr())) {
00553 PyErr_SetObject(PyExc_TypeError, o.ptr());
00554 throw_error_already_set();
00555 }
00556 std::auto_ptr<container_type> c(new container_type);
00557 reg::copyContents(o, *c);
00558 return c.release();
00559 }
00560
00561 static void
00562 copyContents(boost::python::object o, container_type &container )
00563 {
00564 using namespace boost::python;
00565 size_t len = PySequence_Length(o.ptr());
00566 for( size_t i = 0; i < len && i < bitsize; ++i )
00567 {
00568 handle<PyObject> val(PySequence_GetItem(o.ptr(), i));
00569 container.set( i, PyObject_IsTrue(val.get()) );
00570 }
00571 }
00572
00573 static boost::python::list list( container_type &container )
00574 {
00575 boost::python::list l;
00576 for( size_t i = 0; i < bitsize; ++i )
00577 l.append( container[i] ? Py_True : Py_False );
00578 return l;
00579 }
00580
00581 static void set( container_type &container, size_t index, int value )
00582 {
00583 container.set(index, value);
00584 }
00585
00586 static container_type & setbit( container_type &container, size_t index )
00587 {
00588 return container.set(index);
00589 }
00590
00591 static bool eq( container_type const &c1, container_type const &c2 )
00592 {
00593 return c1 == c2;
00594 }
00595
00596 static bool ne( container_type const &c1, container_type const &c2 )
00597 {
00598 return c1 != c2;
00599 }
00600
00601 static container_type or_( container_type const &c1,
00602 container_type const &c2 )
00603 {
00604 return c1 | c2;
00605 }
00606
00607 static container_type xor_( container_type const &c1,
00608 container_type const &c2 )
00609 {
00610 return c1 ^ c2;
00611 }
00612
00613 static container_type and_( container_type const &c1,
00614 container_type const &c2 )
00615 {
00616 return c1 & c2;
00617 }
00618
00619 register_container()
00620 {
00621 using namespace boost::python;
00622 static bool registered = false;
00623 if( registered )
00624 return;
00625 class_<container_type>(toast::type_id<container_type>().name().c_str(),
00626 no_init)
00627 .def( "__init__", make_constructor(®::make) )
00628 .def( "__len__", &container_type::size )
00629 .def( "__getitem__", &container_type::test)
00630 .def( "__setitem__", ®::set )
00631 .def( "__eq__", ®::eq )
00632 .def( "__ne__", ®::ne )
00633 .def( "__or__", ®::or_ )
00634 .def( "__ior__", &container_type::operator |=, return_self<>() )
00635 .def( "__xor__", ®::xor_ )
00636 .def( "__ixor__", &container_type::operator ^=, return_self<>() )
00637 .def( "__and__", ®::and_ )
00638 .def( "__iand__", &container_type::operator &=, return_self<>() )
00639 .def( "__lshift__", &container_type::operator<< )
00640 .def( "__ilshift__", &container_type::operator <<=, return_self<>() )
00641 .def( "__rshift__", &container_type::operator>> )
00642 .def( "__irshift__", &container_type::operator >>=, return_self<>() )
00643 .def( "__invert__", &container_type::operator~ )
00644 .def( "__nonzero__", &container_type::any )
00645 .def( "__str__", &container_type::template
00646 to_string<char, std::char_traits<char>, std::allocator<char> > )
00647 .def( "count", &container_type::count )
00648 .def( "set", static_cast<container_type & (container_type::*)()>(&container_type::set), return_self<>() )
00649 .def( "set", ®::setbit, return_self<>() )
00650 .def( "reset", static_cast<container_type & (container_type::*)()>(&container_type::reset), return_self<>() )
00651 .def( "reset", static_cast<container_type & (container_type::*)(size_t)>(&container_type::reset), return_self<>() )
00652 .def( "flip", static_cast<container_type & (container_type::*)()>(&container_type::flip), return_self<>() )
00653 .def( "flip", static_cast<container_type & (container_type::*)(size_t)>(&container_type::flip), return_self<>() )
00654 ;
00655
00656 implicitly_convertible<object, container_type>();
00657 registered = true;
00658 }
00659 };
00660
00661 }
00662 }
00663
00664 namespace boost { namespace python { namespace converter {
00665
00666 template <typename T>
00667 struct implicit<boost::python::object, std::set<T> >
00668 : ::toast::python::detail::implicit<std::set<T> > {};
00669
00670 template <typename T>
00671 struct implicit<boost::python::object, std::vector<T> >
00672 : ::toast::python::detail::implicit<std::vector<T> > {};
00673
00674 template <typename T>
00675 struct implicit<boost::python::object, std::deque<T> >
00676 : ::toast::python::detail::implicit<std::deque<T> > {};
00677
00678 template <typename T>
00679 struct implicit<boost::python::object, std::list<T> >
00680 : ::toast::python::detail::implicit<std::list<T> > {};
00681
00682 template <typename KeyType, typename ValueType>
00683 struct implicit<boost::python::object, std::map<KeyType, ValueType> >
00684 : ::toast::python::detail::implicit<std::map<KeyType, ValueType> > {};
00685
00686 template <size_t n>
00687 struct implicit<boost::python::object, std::bitset<n> >
00688 : ::toast::python::detail::implicit<std::bitset<n> > {};
00689
00690 }
00691 }
00692 }
00693
00694 #endif // toast_python_container_hpp_INCLUDED
00695
00696