mirror of
https://github.com/boostorg/python.git
synced 2026-01-19 16:32:16 +00:00
127 lines
4.0 KiB
C++
127 lines
4.0 KiB
C++
/*
|
|
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 <string>
|
|
#include <vector>
|
|
#include <boost/python/class_builder.hpp>
|
|
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<MillerIndex> VMIx;
|
|
public:
|
|
void add(const MillerIndex& MIx) { VMIx.push_back(MIx); }
|
|
MillerIndex get(const 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<const MillerIndex&>)
|
|
{
|
|
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<int>());
|
|
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<MillerIndex>)
|
|
{
|
|
return from_python(p, python::type<const MillerIndex&>());
|
|
}
|
|
|
|
// 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<IndexingSet> 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
|
|
}
|
|
}
|