From 2a96c9f9eea85095c6c1ff889d3c264123e72945 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 21 Mar 2001 02:33:27 +0000 Subject: [PATCH] temp file before branching [SVN r9621] --- example/do_it_yourself_converters.cpp | 126 ++++++++++++++++++++++ example/simple_vector.cpp | 101 +++++++++++++++++ example/test_do_it_yourself_converters.py | 22 ++++ example/test_simple_vector.py | 35 ++++++ 4 files changed, 284 insertions(+) create mode 100644 example/do_it_yourself_converters.cpp create mode 100644 example/simple_vector.cpp create mode 100644 example/test_do_it_yourself_converters.py create mode 100644 example/test_simple_vector.py diff --git a/example/do_it_yourself_converters.cpp b/example/do_it_yourself_converters.cpp new file mode 100644 index 00000000..ad31f708 --- /dev/null +++ b/example/do_it_yourself_converters.cpp @@ -0,0 +1,126 @@ +/* + 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(do_it_yourself_converters) +{ + try + { + // Create an object representing this extension module. + python::module_builder this_module("do_it_yourself_converters"); + + // 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 + } +} diff --git a/example/simple_vector.cpp b/example/simple_vector.cpp new file mode 100644 index 00000000..f18f2cad --- /dev/null +++ b/example/simple_vector.cpp @@ -0,0 +1,101 @@ +#include +namespace python = boost::python; + +namespace { // Avoid cluttering the global namespace. + + // A wrapper is used to define additional constructors. + // + struct vector_double_wrapper: std::vector + { + // Tell the compiler how to convert a base class object to + // this wrapper object. + vector_double_wrapper(PyObject*, const std::vector& vd) + : std::vector(vd) {} + + vector_double_wrapper(PyObject* self) + : std::vector() {} + + vector_double_wrapper(PyObject* self, int n) + : std::vector(n) {} + + vector_double_wrapper(PyObject* self, python::tuple tuple) + : std::vector(tuple.size()) + { + std::vector::iterator vd = begin(); + for (int i = 0; i < tuple.size(); i++) + vd[i] = BOOST_PYTHON_CONVERSION::from_python(tuple[i].get(), + python::type()); + } + }; + + double getitem(const std::vector& vd, std::size_t key) { + return vd[key]; + } + + void setitem(std::vector& vd, std::size_t key, double d) { + std::vector::iterator vditer = vd.begin(); + vditer[key] = d; + } + + void delitem(std::vector& vd, std::size_t key) { + std::vector::iterator vditer = vd.begin(); + vd.erase(&vditer[key]); + } + + // Convert vector_double to a regular Python tuple. + // + python::tuple as_tuple(const std::vector& vd) + { + python::tuple t(vd.size()); + for (int i = 0; i < vd.size(); i++) t.set_item(i, + python::ref(BOOST_PYTHON_CONVERSION::to_python(vd[i]))); + return t; + } + + // Function returning a vector_double object to Python. + // + std::vector foo(int n) + { + std::vector vd(n); + std::vector::iterator vditer = vd.begin(); + for (int i = 0; i < n; i++) vditer[i] = double(i); + return vd; + } + + // Same as foo(), but avoid copying on return. + // + std::auto_ptr > bar(int n) + { + std::auto_ptr > vdptr(new std::vector(n)); + std::vector::iterator vditer = vdptr->begin(); + for (int i = 0; i < n; i++) vditer[i] = double(10 * i); + return vdptr; + } +} + +BOOST_PYTHON_MODULE_INIT(simple_vector) +{ + try + { + python::module_builder this_module("simple_vector"); + + python::class_builder, vector_double_wrapper> + vector_double(this_module, "vector_double"); + + vector_double.def(python::constructor<>()); + vector_double.def(python::constructor()); + vector_double.def(python::constructor()); + vector_double.def(&std::vector::size, "__len__"); + vector_double.def(getitem, "__getitem__"); + vector_double.def(setitem, "__setitem__"); + vector_double.def(delitem, "__delitem__"); + vector_double.def(as_tuple, "as_tuple"); + + this_module.def(foo, "foo"); + this_module.def(bar, "bar"); + } + catch(...) + { + python::handle_exception(); // Deal with the exception for Python + } +} diff --git a/example/test_do_it_yourself_converters.py b/example/test_do_it_yourself_converters.py new file mode 100644 index 00000000..e256c614 --- /dev/null +++ b/example/test_do_it_yourself_converters.py @@ -0,0 +1,22 @@ +r'''>>> import do_it_yourself_converters + >>> ixset = do_it_yourself_converters.IndexingSet() + >>> ixset.add((1,2,3)) + >>> ixset.add((4,5,6)) + >>> ixset.add((7,8,9)) + >>> print ixset.get(0) + (1, 2, 3) + >>> print ixset.get(1) + (4, 5, 6) + >>> print ixset.get(2) + (7, 8, 9) +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_do_it_yourself_converters + doctest.testmod(test_do_it_yourself_converters) + +if __name__ == '__main__': + run() diff --git a/example/test_simple_vector.py b/example/test_simple_vector.py new file mode 100644 index 00000000..a19e205b --- /dev/null +++ b/example/test_simple_vector.py @@ -0,0 +1,35 @@ +r'''>>> import simple_vector + >>> v=simple_vector.vector_double() + >>> print v.as_tuple() + () + >>> v=simple_vector.vector_double(5) + >>> print v.as_tuple() + (0.0, 0.0, 0.0, 0.0, 0.0) + >>> print len(v) + 5 + >>> v=simple_vector.vector_double((3,4,5)) + >>> print v.as_tuple() + (3.0, 4.0, 5.0) + >>> print v[1] + 4.0 + >>> v[1] = 40 + >>> print v.as_tuple() + (3.0, 40.0, 5.0) + >>> del v[1] + >>> print v.as_tuple() + (3.0, 5.0) + >>> print simple_vector.foo(11).as_tuple() + (0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0) + >>> print simple_vector.bar(12).as_tuple() + (0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0) +''' + +def run(args = None): + if args is not None: + import sys + sys.argv = args + import doctest, test_simple_vector + doctest.testmod(test_simple_vector) + +if __name__ == '__main__': + run()