/* This example shows how to convert a class from and to native Python objects, such as tuples. We do not want to expose the helper class MillerIndex as an Extension Class. However, in order to simplify the wrapper code, we want to define from_python() and to_python() functions for class MillerIndex. Consider the alternatives: - Expose MillerIndex as an Extension Class. We need a constructor MillerIndex(python::tuple). Python function calls become more complex: foo(MillerIndex((1,2,3)) instead of foo((1,2,3)) We need a method such as MillerIndex().as_tuple(). - Define a wrapper function for each function that we want to expose, e.g.: void add(const IndexingSet& ixset, const python::tuple PyMIx) The first alternative introduces a new type that the user has to deal with. Other modules using Miller indices might organize them in different ways, for example to increase runtime efficiency for important procedures. This means, the user has to know how to convert between the different kinds of Miller index representations. This can quickly become a nuisance. Relying on native Python data structures minimizes the number of special types the user has to learn and convert. Of course, this argument is only valid for small and relatively simply classes. If there are many member functions with MillerIndex arguments, the second alternative is impractical, and concentrating the conversion mechanism in one central place is essential for code maintainability. An added benefit is that more convenient (smarter) conversion functions can be provided without cluttering the rest of the wrapper code. */ #include #include #include namespace python = boost::python; namespace { // Avoid cluttering the global namespace. // The helper class. // class MillerIndex { public: int v[3]; }; // The main class. Imagine that there are MANY member functions // like add() and get(). // class IndexingSet { private: std::vector VMIx; public: void add(const MillerIndex& MIx) { VMIx.push_back(MIx); } MillerIndex get(std::size_t i) const { return VMIx[i]; } }; } BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE // Convert a Python tuple to a MillerIndex object. // MillerIndex from_python(PyObject* p, python::type) { python::tuple tup = python::tuple(python::ref(p, python::ref::increment_count)); if (tup.size() != 3) { PyErr_SetString(PyExc_ValueError, "expecting exactly 3 values in tuple."); throw python::error_already_set(); } MillerIndex result; for (int i = 0; i < 3; i++) result.v[i] = from_python(tup[i].get(), python::type()); return result; } // Similar conversion for MillerIndex objects passed by value. // Not actually used, but included to show the principle. // MillerIndex from_python(PyObject* p, python::type) { return from_python(p, python::type()); } // Convert a MillerIndex object to a Python tuple. // PyObject* to_python(const MillerIndex& hkl) { python::tuple result(3); for (int i = 0; i < 3; i++) result.set_item(i, python::ref(to_python(hkl.v[i]))); return result.reference().release(); } BOOST_PYTHON_END_CONVERSION_NAMESPACE BOOST_PYTHON_MODULE_INIT(getting_started5) { try { // Create an object representing this extension module. python::module_builder this_module("getting_started5"); // Create the Python type object for our extension class. python::class_builder ixset_class(this_module, "IndexingSet"); // Add the __init__ function. ixset_class.def(python::constructor<>()); // Add the member functions. ixset_class.def(&IndexingSet::add, "add"); ixset_class.def(&IndexingSet::get, "get"); } catch(...) { python::handle_exception(); // Deal with the exception for Python } }