1 // (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com>
2 
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)
6 
7 //  Authors: Douglas Gregor
8 #ifndef BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP
9 #define BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP
10 
11 /** @file skeleton_and_content.hpp
12  *
13  *  This file reflects the skeleton/content facilities into Python.
14  */
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>
21 
22 
23 namespace boost { namespace mpi { namespace python {
24 
25 /**
26  * INTERNAL ONLY
27  *
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.
31  */
32 class content : public boost::mpi::content
33 {
34   typedef boost::mpi::content inherited;
35 
36  public:
content(const inherited & base,boost::python::object object)37   content(const inherited& base, boost::python::object object)
38     : inherited(base), object(object) { }
39 
base()40   inherited&       base()       { return *this; }
base() const41   const inherited& base() const { return *this; }
42 
43   boost::python::object object;
44 };
45 
46 /**
47  * INTERNAL ONLY
48  *
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()
55  * function.
56  */
57 class skeleton_proxy_base
58 {
59 public:
skeleton_proxy_base(const boost::python::object & object)60   skeleton_proxy_base(const boost::python::object& object) : object(object) { }
61 
62   boost::python::object object;
63 };
64 
65 /**
66  * INTERNAL ONLY
67  *
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.
73  */
74 template<typename T>
75 class skeleton_proxy : public skeleton_proxy_base
76 {
77  public:
skeleton_proxy(const boost::python::object & object)78   skeleton_proxy(const boost::python::object& object)
79     : skeleton_proxy_base(object) { }
80 };
81 
82 namespace detail {
83   using boost::python::object;
84   using boost::python::extract;
85 
86   extern BOOST_MPI_DECL boost::python::object skeleton_proxy_base_type;
87 
88   template<typename T>
89   struct skeleton_saver
90   {
91     void
operator ()boost::mpi::python::detail::skeleton_saver92     operator()(packed_oarchive& ar, const object& obj, const unsigned int)
93     {
94       packed_skeleton_oarchive pso(ar);
95       pso << extract<T&>(obj.attr("object"))();
96     }
97   };
98 
99   template<typename T>
100   struct skeleton_loader
101   {
102     void
operator ()boost::mpi::python::detail::skeleton_loader103     operator()(packed_iarchive& ar, object& obj, const unsigned int)
104     {
105       packed_skeleton_iarchive psi(ar);
106       extract<skeleton_proxy<T>&> proxy(obj);
107       if (!proxy.check())
108         obj = object(skeleton_proxy<T>(object(T())));
109 
110       psi >> extract<T&>(obj.attr("object"))();
111     }
112   };
113 
114   /**
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.
118    */
119   struct skeleton_content_handler {
120     function1<object, const object&> get_skeleton_proxy;
121     function1<content, const object&> get_content;
122   };
123 
124   /**
125    * A function object that extracts the skeleton from of a Python
126    * object, which is actually a wrapped C++ object of type T.
127    */
128   template<typename T>
129   struct do_get_skeleton_proxy
130   {
operator ()boost::mpi::python::detail::do_get_skeleton_proxy131     object operator()(object value) {
132       return object(skeleton_proxy<T>(value));
133     }
134   };
135 
136   /**
137    * A function object that extracts the content of a Python object,
138    * which is actually a wrapped C++ object of type T.
139    */
140   template<typename T>
141   struct do_get_content
142   {
operator ()boost::mpi::python::detail::do_get_content143     content operator()(object value_obj) {
144       T& value = extract<T&>(value_obj)();
145       return content(boost::mpi::get_content(value), value_obj);
146     }
147   };
148 
149   /**
150    * Determine if a skeleton and content handler for @p type has
151    * already been registered.
152    */
153   BOOST_MPI_PYTHON_DECL bool
154   skeleton_and_content_handler_registered(PyTypeObject* type);
155 
156   /**
157    * Register a skeleton/content handler with a particular Python type
158    * (which actually wraps a C++ type).
159    */
160   BOOST_MPI_PYTHON_DECL void
161   register_skeleton_and_content_handler(PyTypeObject*,
162                                         const skeleton_content_handler&);
163 } // end namespace detail
164 
165 template<typename T>
register_skeleton_and_content(const T & value,PyTypeObject * type)166 void register_skeleton_and_content(const T& value, PyTypeObject* type)
167 {
168   using boost::python::detail::direct_serialization_table;
169   using boost::python::detail::get_direct_serialization_table;
170   using namespace boost::python;
171 
172   // Determine the type
173   if (!type)
174     type = object(value).ptr()->ob_type;
175 
176   // Don't re-register the same type.
177   if (detail::skeleton_and_content_handler_registered(type))
178     return;
179 
180   // Register the skeleton proxy type
181   {
182     boost::python::scope proxy_scope(detail::skeleton_proxy_base_type);
183     std::string name("skeleton_proxy<");
184     name += typeid(T).name();
185     name += ">";
186     class_<skeleton_proxy<T>, bases<skeleton_proxy_base> >(name.c_str(),
187                                                            no_init);
188   }
189 
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)));
197 
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);
205 }
206 
207 } } } // end namespace boost::mpi::python
208 
209 #endif // BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP
210