skeleton_and_content.hpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com>
  2. // Use, modification and distribution is subject to the Boost Software
  3. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. // Authors: Douglas Gregor
  6. #ifndef BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP
  7. #define BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP
  8. /** @file skeleton_and_content.hpp
  9. *
  10. * This file reflects the skeleton/content facilities into Python.
  11. */
  12. #include <boost/python.hpp>
  13. #include <boost/mpi.hpp>
  14. #include <boost/function/function1.hpp>
  15. #define BOOST_MPI_PYTHON_FORWARD_ONLY
  16. #include <boost/mpi/python.hpp>
  17. #include <boost/mpi/python/serialize.hpp>
  18. namespace boost { namespace mpi { namespace python {
  19. /**
  20. * INTERNAL ONLY
  21. *
  22. * This @c content class is a wrapper around the C++ "content"
  23. * retrieved from get_content. This wrapper is only needed to store a
  24. * copy of the Python object on which get_content() was called.
  25. */
  26. class content : public boost::mpi::content
  27. {
  28. typedef boost::mpi::content inherited;
  29. public:
  30. content(const inherited& base, boost::python::object object)
  31. : inherited(base), object(object) { }
  32. inherited& base() { return *this; }
  33. const inherited& base() const { return *this; }
  34. boost::python::object object;
  35. };
  36. /**
  37. * INTERNAL ONLY
  38. *
  39. * A class specific to the Python bindings that mimics the behavior of
  40. * the skeleton_proxy<T> template. In the case of Python skeletons, we
  41. * only need to know the object (and its type) to transmit the
  42. * skeleton. This is the only user-visible skeleton proxy type,
  43. * although instantiations of its derived classes (@c
  44. * skeleton_proxy<T>) will be returned from the Python skeleton()
  45. * function.
  46. */
  47. class skeleton_proxy_base
  48. {
  49. public:
  50. skeleton_proxy_base(const boost::python::object& object) : object(object) { }
  51. boost::python::object object;
  52. };
  53. /**
  54. * INTERNAL ONLY
  55. *
  56. * The templated @c skeleton_proxy class represents a skeleton proxy
  57. * in Python. The only data is stored in the @c skeleton_proxy_base
  58. * class (which is the type actually exposed as @c skeleton_proxy in
  59. * Python). However, the type of @c skeleton_proxy<T> is important for
  60. * (de-)serialization of @c skeleton_proxy<T>'s for transmission.
  61. */
  62. template<typename T>
  63. class skeleton_proxy : public skeleton_proxy_base
  64. {
  65. public:
  66. skeleton_proxy(const boost::python::object& object)
  67. : skeleton_proxy_base(object) { }
  68. };
  69. namespace detail {
  70. using boost::python::object;
  71. using boost::python::extract;
  72. extern BOOST_MPI_DECL boost::python::object skeleton_proxy_base_type;
  73. template<typename T>
  74. struct skeleton_saver
  75. {
  76. void
  77. operator()(packed_oarchive& ar, const object& obj, const unsigned int)
  78. {
  79. packed_skeleton_oarchive pso(ar);
  80. pso << extract<T&>(obj.attr("object"))();
  81. }
  82. };
  83. template<typename T>
  84. struct skeleton_loader
  85. {
  86. void
  87. operator()(packed_iarchive& ar, object& obj, const unsigned int)
  88. {
  89. packed_skeleton_iarchive psi(ar);
  90. extract<skeleton_proxy<T>&> proxy(obj);
  91. if (!proxy.check())
  92. obj = object(skeleton_proxy<T>(object(T())));
  93. psi >> extract<T&>(obj.attr("object"))();
  94. }
  95. };
  96. /**
  97. * The @c skeleton_content_handler structure contains all of the
  98. * information required to extract a skeleton and content from a
  99. * Python object with a certain C++ type.
  100. */
  101. struct skeleton_content_handler {
  102. function1<object, const object&> get_skeleton_proxy;
  103. function1<content, const object&> get_content;
  104. };
  105. /**
  106. * A function object that extracts the skeleton from of a Python
  107. * object, which is actually a wrapped C++ object of type T.
  108. */
  109. template<typename T>
  110. struct do_get_skeleton_proxy
  111. {
  112. object operator()(object value) {
  113. return object(skeleton_proxy<T>(value));
  114. }
  115. };
  116. /**
  117. * A function object that extracts the content of a Python object,
  118. * which is actually a wrapped C++ object of type T.
  119. */
  120. template<typename T>
  121. struct do_get_content
  122. {
  123. content operator()(object value_obj) {
  124. T& value = extract<T&>(value_obj)();
  125. return content(boost::mpi::get_content(value), value_obj);
  126. }
  127. };
  128. /**
  129. * Determine if a skeleton and content handler for @p type has
  130. * already been registered.
  131. */
  132. BOOST_MPI_PYTHON_DECL bool
  133. skeleton_and_content_handler_registered(PyTypeObject* type);
  134. /**
  135. * Register a skeleton/content handler with a particular Python type
  136. * (which actually wraps a C++ type).
  137. */
  138. BOOST_MPI_PYTHON_DECL void
  139. register_skeleton_and_content_handler(PyTypeObject*,
  140. const skeleton_content_handler&);
  141. } // end namespace detail
  142. template<typename T>
  143. void register_skeleton_and_content(const T& value, PyTypeObject* type)
  144. {
  145. using boost::python::detail::direct_serialization_table;
  146. using boost::python::detail::get_direct_serialization_table;
  147. using namespace boost::python;
  148. // Determine the type
  149. if (!type)
  150. type = object(value).ptr()->ob_type;
  151. // Don't re-register the same type.
  152. if (detail::skeleton_and_content_handler_registered(type))
  153. return;
  154. // Register the skeleton proxy type
  155. {
  156. boost::python::scope proxy_scope(detail::skeleton_proxy_base_type);
  157. std::string name("skeleton_proxy<");
  158. name += typeid(T).name();
  159. name += ">";
  160. class_<skeleton_proxy<T>, bases<skeleton_proxy_base> >(name.c_str(),
  161. no_init);
  162. }
  163. // Register the saver and loader for the associated skeleton and
  164. // proxy, to allow (de-)serialization of skeletons via the proxy.
  165. direct_serialization_table<packed_iarchive, packed_oarchive>& table =
  166. get_direct_serialization_table<packed_iarchive, packed_oarchive>();
  167. table.register_type(detail::skeleton_saver<T>(),
  168. detail::skeleton_loader<T>(),
  169. skeleton_proxy<T>(object(value)));
  170. // Register the rest of the skeleton/content mechanism, including
  171. // handlers that extract a skeleton proxy from a Python object and
  172. // extract the content from a Python object.
  173. detail::skeleton_content_handler handler;
  174. handler.get_skeleton_proxy = detail::do_get_skeleton_proxy<T>();
  175. handler.get_content = detail::do_get_content<T>();
  176. detail::register_skeleton_and_content_handler(type, handler);
  177. }
  178. } } } // end namespace boost::mpi::python
  179. #endif // BOOST_MPI_PYTHON_SKELETON_AND_CONTENT_HPP