[C++-sig] export std::set
Andreas Beyer
mail at a-beyer.de
Wed May 3 15:26:40 CEST 2006
Hi,
I created an interface for exporting std::set<> to python. It is still
incomplete, but I figured it might be usefull for others to adopt to
their needs.
The idea of the interface somewhat resembles the indexing_suite. There
are two template functions for exporting set<>-types:
export_set publishes an interface for mutable sets to python (like
python's "set").
export_frozenset publishes the immutable equivalent. Both functions take
the desired python names of the classes as arguments.
You can use the functions as follows:
BOOST_PYTHON_MODULE(your_module) {
export_set<std::string>("StrSet"); // use set's key-type as template
argument
}
You cannot mix set and frozenset for the same C++ type in one module,
because then the mapping of the type to python becomes ambigious.
Subclassing the original set<>-types might help (Untested!).
In python you can do things like this:
>>> from set_test import *
>>>
>>> s1 = StrSet() # create set<string> from python
>>> s1.add('a') # add a new element
>>> s1.add('b')
>>> s2 = make_str_set('b', 'c') # create & return set from C++
>>> list(s1)
['a', 'b']
>>> list(s2)
['b', 'c']
>>> list(s1 & s2) # set intersection
['b']
>>> list(s1 | s2) # set union
['a', 'b', 'c']
>>> list(s1 - s2) # set difference
['a']
>>> list(s1 ^ s2) # symmetric set difference
['a', 'c']
>>> s1.remove('a')
>>> s1.remove('x') # attempt removing non-existent element
Traceback (most recent call last):
File "<stdin>", line 1, in ?
RuntimeError: unidentifiable C++ exception
>>>
>>> d = {s1:0} # mutable object, no hashing allowed
Traceback (most recent call last):
File "<stdin>", line 1, in ?
RuntimeError: unidentifiable C++ exception
>>>
>>> # using immutable sets
>>> s1 = make_int_set(0,1) # create immutable set in C++
>>> s2 = FrozenIntSet()
>>> s3 = make_int_set(0, 1)
>>> s1 == s2
False
>>> s1 == s3
True
>>> s1 < s2
False
>>> s2 < s1
True
>>> d = {s1:1, s2:2, s3:3} # immutable sets are hashable
>>>
Please, excuse the kinky way of printing the sets' contents, but I
didn't create a __str__() method yet. Also, there is no python
constructor accepting lists/tuples/iterators. Send me the code if you
write one!
Eventually, it would be preferable if set<> would be directly translated
into python's set types. However, I see no easy solution and some
issues, e.g. how would such automated converter decide, if the given
C++-set should become python-set or python-frozenset?
Andreas
Here is the complete code:
(Please, don't mind my Java-style of using '{'. It is hard-coded in my
fingers ;-) )
--- begin py_set.hpp ---
#include <algorithm>
#include <exception>
#include <set>
#include <typeinfo>
#include <boost/python.hpp>
using namespace boost;
template<class KeyType> class py_set : public std::set<KeyType>,
public python::wrapper< std::set<KeyType> > {
public:
// some typedefs for convinience
typedef std::set<KeyType> source_type;
typedef py_set<KeyType> wrap_type;
// constructors
py_set<KeyType> () {};
py_set<KeyType> (const source_type& s )
{ insert(s.begin(), s.end()); }
// element access
bool contains(const KeyType key)
{ return count(key)>0; }
// we must define add() for it gets explicit argument types
void add(const KeyType key)
{ insert(key); }
void remove(const KeyType key)
// improve error handling here
{ if (!contains(key)) throw "element not in set"; erase(key); }
// set operations
source_type set_union(wrap_type &other) {
source_type result;
std::set_union(begin(), end(), other.begin(), other.end(),
inserter(result, result.begin()));
return result;
}
source_type set_intersection(wrap_type &other) {
source_type result;
std::set_intersection(begin(), end(), other.begin(),
other.end(),
inserter(result, result.begin()));
return result;
}
source_type set_difference(wrap_type &other) {
source_type result;
std::set_difference(begin(), end(), other.begin(),
other.end(),
inserter(result, result.begin()));
return result;
}
source_type set_symmetric_difference(wrap_type &other) {
source_type result;
std::set_symmetric_difference(begin(), end(),
other.begin(), other.end(),
inserter(result, result.begin()));
return result;
}
};
inline void block_hashing(python::object) {
// do something more intelligent here
throw "objects of this type are unhashable";
}
// export mutable set
template<class KeyType> void
export_set(const char* py_name) {
typedef py_set<KeyType> set_T;
python::class_<set_T > (py_name, "mutable set")
.def("__len__", &set_T::size)
.def("__contains__", &set_T::contains)
.def("add", &set_T::add, "add element")
.def("__delitem__", &set_T::remove)
.def("remove", &set_T::remove, "remove element")
.def("__iter__", python::iterator<set_T> ())
.def("__hash__", &block_hashing)
.def("union", &set_T::set_union, "set union")
.def("__or__", &set_T::set_union, "set union")
.def("intersection", &set_T::set_intersection, "set intersection")
.def("__and__", &set_T::set_intersection, "set intersection")
.def("difference", &set_T::set_difference, "elements not in
second set")
.def("__sub__", &set_T::set_difference, "set difference")
.def("symmetric_difference", &set_T::set_symmetric_difference,
"elements unique to either set")
.def("__xor__", &set_T::set_symmetric_difference,
"symmetric set difference")
;
python::implicitly_convertible<py_set<KeyType>, std::set<KeyType> >();
python::implicitly_convertible<std::set<KeyType>, py_set<KeyType> >();
}
// export immutable set
template<class KeyType> void
export_frozenset(const char* py_name) {
typedef py_set<KeyType> set_T;
python::class_<set_T > (py_name, "immutable set")
.def("__len__", &set_T::size)
.def("__contains__", &set_T::contains)
.def("__iter__", python::iterator<set_T> ())
.def(python::self < python::self)
.def(python::self == python::self)
.def("union", &set_T::set_union, "set union")
.def("__or__", &set_T::set_union, "set union")
.def("intersection", &set_T::set_intersection, "set intersection")
.def("__and__", &set_T::set_intersection, "set intersection")
.def("difference", &set_T::set_difference, "elements not in
second set")
.def("__sub__", &set_T::set_difference, "set different")
.def("symmetric_difference", &set_T::set_symmetric_difference,
"elements unique to either set")
.def("__xor__", &set_T::set_symmetric_difference,
"symmetric set different")
;
python::implicitly_convertible<py_set<KeyType>, std::set<KeyType> >();
python::implicitly_convertible<std::set<KeyType>, py_set<KeyType> >();
}
--- end py_set.hpp ---
--- begin set_test.cpp ---
#include "py_set.hpp"
// examples using the above code:
std::set<std::string> make_str_set(std::string a, std::string b) {
std::set<std::string> s;
s.insert(a);
s.insert(b);
return s;
}
std::set<int> make_int_set(int a, int b) {
std::set<int> s;
s.insert(a);
s.insert(b);
return s;
}
BOOST_PYTHON_MODULE(set_test) {
export_set<std::string>("StrSet");
// don't export the same C++ type twice
// export_frozenset<std::string>("FrozenStrSet");
export_frozenset<int>("FrozenIntSet");
// create set<> instances in C++
python::def("make_str_set", &make_str_set);
python::def("make_int_set", &make_int_set);
}
--- end set_test.cpp ---
--
Dr. Andreas Beyer
mail at a-beyer.de
www.andreas-beyer.de
More information about the Cplusplus-sig
mailing list