[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