1 // (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com>
3 // Use, modification and distribution is subject to the Boost Software
4 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 // Authors: Douglas Gregor
8 #ifndef BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP
9 #define BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP
11 /** @file skeleton_and_content.hpp
13 * This file reflects the skeleton/content facilities into Python.
15 #include <boost/python.hpp>
16 #include <boost/mpi.hpp>
17 #include <boost/function/function1.hpp>
18 #define BOOST_MPI_PYTHON_FORWARD_ONLY
19 #include <boost/mpi/python.hpp>
20 #include <boost/mpi/python/serialize.hpp>
23 namespace boost { namespace mpi { namespace python {
28 * This @c content class is a wrapper around the C++ "content"
29 * retrieved from get_content. This wrapper is only needed to store a
30 * copy of the Python object on which get_content() was called.
32 class content : public boost::mpi::content
34 typedef boost::mpi::content inherited;
37 content(const inherited& base, boost::python::object object)
38 : inherited(base), object(object) { }
40 inherited& base() { return *this; }
41 const inherited& base() const { return *this; }
43 boost::python::object object;
49 * A class specific to the Python bindings that mimics the behavior of
50 * the skeleton_proxy<T> template. In the case of Python skeletons, we
51 * only need to know the object (and its type) to transmit the
52 * skeleton. This is the only user-visible skeleton proxy type,
53 * although instantiations of its derived classes (@c
54 * skeleton_proxy<T>) will be returned from the Python skeleton()
57 class skeleton_proxy_base
60 skeleton_proxy_base(const boost::python::object& object) : object(object) { }
62 boost::python::object object;
68 * The templated @c skeleton_proxy class represents a skeleton proxy
69 * in Python. The only data is stored in the @c skeleton_proxy_base
70 * class (which is the type actually exposed as @c skeleton_proxy in
71 * Python). However, the type of @c skeleton_proxy<T> is important for
72 * (de-)serialization of @c skeleton_proxy<T>'s for transmission.
75 class skeleton_proxy : public skeleton_proxy_base
78 skeleton_proxy(const boost::python::object& object)
79 : skeleton_proxy_base(object) { }
83 using boost::python::object;
84 using boost::python::extract;
86 extern BOOST_MPI_DECL boost::python::object skeleton_proxy_base_type;
92 operator()(packed_oarchive& ar, const object& obj, const unsigned int)
94 packed_skeleton_oarchive pso(ar);
95 pso << extract<T&>(obj.attr("object"))();
100 struct skeleton_loader
103 operator()(packed_iarchive& ar, object& obj, const unsigned int)
105 packed_skeleton_iarchive psi(ar);
106 extract<skeleton_proxy<T>&> proxy(obj);
108 obj = object(skeleton_proxy<T>(object(T())));
110 psi >> extract<T&>(obj.attr("object"))();
115 * The @c skeleton_content_handler structure contains all of the
116 * information required to extract a skeleton and content from a
117 * Python object with a certain C++ type.
119 struct skeleton_content_handler {
120 function1<object, const object&> get_skeleton_proxy;
121 function1<content, const object&> get_content;
125 * A function object that extracts the skeleton from of a Python
126 * object, which is actually a wrapped C++ object of type T.
129 struct do_get_skeleton_proxy
131 object operator()(object value) {
132 return object(skeleton_proxy<T>(value));
137 * A function object that extracts the content of a Python object,
138 * which is actually a wrapped C++ object of type T.
141 struct do_get_content
143 content operator()(object value_obj) {
144 T& value = extract<T&>(value_obj)();
145 return content(boost::mpi::get_content(value), value_obj);
150 * Determine if a skeleton and content handler for @p type has
151 * already been registered.
153 BOOST_MPI_PYTHON_DECL bool
154 skeleton_and_content_handler_registered(PyTypeObject* type);
157 * Register a skeleton/content handler with a particular Python type
158 * (which actually wraps a C++ type).
160 BOOST_MPI_PYTHON_DECL void
161 register_skeleton_and_content_handler(PyTypeObject*,
162 const skeleton_content_handler&);
163 } // end namespace detail
166 void register_skeleton_and_content(const T& value, PyTypeObject* type)
168 using boost::python::detail::direct_serialization_table;
169 using boost::python::detail::get_direct_serialization_table;
170 using namespace boost::python;
172 // Determine the type
174 type = object(value).ptr()->ob_type;
176 // Don't re-register the same type.
177 if (detail::skeleton_and_content_handler_registered(type))
180 // Register the skeleton proxy type
182 boost::python::scope proxy_scope(detail::skeleton_proxy_base_type);
183 std::string name("skeleton_proxy<");
184 name += typeid(T).name();
186 class_<skeleton_proxy<T>, bases<skeleton_proxy_base> >(name.c_str(),
190 // Register the saver and loader for the associated skeleton and
191 // proxy, to allow (de-)serialization of skeletons via the proxy.
192 direct_serialization_table<packed_iarchive, packed_oarchive>& table =
193 get_direct_serialization_table<packed_iarchive, packed_oarchive>();
194 table.register_type(detail::skeleton_saver<T>(),
195 detail::skeleton_loader<T>(),
196 skeleton_proxy<T>(object(value)));
198 // Register the rest of the skeleton/content mechanism, including
199 // handlers that extract a skeleton proxy from a Python object and
200 // extract the content from a Python object.
201 detail::skeleton_content_handler handler;
202 handler.get_skeleton_proxy = detail::do_get_skeleton_proxy<T>();
203 handler.get_content = detail::do_get_content<T>();
204 detail::register_skeleton_and_content_handler(type, handler);
207 } } } // end namespace boost::mpi::python
209 #endif // BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP