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 
9 /** @file skeleton_and_content.cpp
10  *
11  *  This file reflects the skeleton/content facilities into Python.
12  */
13 #include <boost/mpi/python/skeleton_and_content.hpp>
14 #include <boost/mpi/python/serialize.hpp>
15 #include <boost/python/list.hpp>
16 #include <typeinfo>
17 #include <list>
18 #include "utility.hpp"
19 #include "request_with_value.hpp"
20 
21 using namespace boost::python;
22 using namespace boost::mpi;
23 
24 namespace boost { namespace mpi { namespace python {
25 
26 namespace detail {
27   typedef std::map<PyTypeObject*, skeleton_content_handler>
28     skeleton_content_handlers_type;
29 
30 // We're actually importing skeleton_content_handlers from skeleton_and_content.cpp.
31 #if defined(BOOST_HAS_DECLSPEC) && (defined(BOOST_MPI_PYTHON_DYN_LINK) || defined(BOOST_ALL_DYN_LINK))
32 #  define BOOST_SC_DECL __declspec(dllimport)
33 #else
34 #  define BOOST_SC_DECL
35 #endif
36 
37   extern BOOST_SC_DECL skeleton_content_handlers_type skeleton_content_handlers;
38 }
39 
40 /**
41  * An exception that will be thrown when the object passed to the
42  * Python version of skeleton() does not have a skeleton.
43  */
44 struct object_without_skeleton : public std::exception {
object_without_skeletonboost::mpi::python::object_without_skeleton45   explicit object_without_skeleton(object value) : value(value) { }
~object_without_skeletonboost::mpi::python::object_without_skeleton46   virtual ~object_without_skeleton() throw() { }
47 
48   object value;
49 };
50 
object_without_skeleton_str(const object_without_skeleton & e)51 str object_without_skeleton_str(const object_without_skeleton& e)
52 {
53   return str("\nThe skeleton() or get_content() function was invoked for a Python\n"
54              "object that is not supported by the Boost.MPI skeleton/content\n"
55              "mechanism. To transfer objects via skeleton/content, you must\n"
56              "register the C++ type of this object with the C++ function:\n"
57              "  boost::mpi::python::register_skeleton_and_content()\n"
58              "Object: " + str(e.value) + "\n");
59 }
60 
61 /**
62  * Extract the "skeleton" from a Python object. In truth, all we're
63  * doing at this point is verifying that the object is a C++ type that
64  * has been registered for the skeleton/content mechanism.
65  */
skeleton(object value)66 object skeleton(object value)
67 {
68   PyTypeObject* type = value.ptr()->ob_type;
69   detail::skeleton_content_handlers_type::iterator pos =
70     detail::skeleton_content_handlers.find(type);
71   if (pos == detail::skeleton_content_handlers.end())
72     throw object_without_skeleton(value);
73   else
74     return pos->second.get_skeleton_proxy(value);
75 }
76 
77 /**
78  * Extract the "content" from a Python object, which must be a C++
79  * type that has been registered for the skeleton/content mechanism.
80  */
get_content(object value)81 content get_content(object value)
82 {
83   PyTypeObject* type = value.ptr()->ob_type;
84   detail::skeleton_content_handlers_type::iterator pos =
85     detail::skeleton_content_handlers.find(type);
86   if (pos == detail::skeleton_content_handlers.end())
87     throw object_without_skeleton(value);
88   else
89     return pos->second.get_content(value);
90 }
91 
92 /// Send the content part of a Python object.
93 void
communicator_send_content(const communicator & comm,int dest,int tag,const content & c)94 communicator_send_content(const communicator& comm, int dest, int tag,
95                           const content& c)
96 {
97   comm.send(dest, tag, c.base());
98 }
99 
100 /// Receive the content of a Python object. We return the object
101 /// received, not the content wrapper.
102 object
communicator_recv_content(const communicator & comm,int source,int tag,const content & c,bool return_status)103 communicator_recv_content(const communicator& comm, int source, int tag,
104                           const content& c, bool return_status)
105 {
106   using boost::python::make_tuple;
107 
108   status stat = comm.recv(source, tag, c.base());
109   if (return_status)
110     return make_tuple(c.object, stat);
111   else
112     return c.object;
113 }
114 
115 /// Receive the content of a Python object. The request object's value
116 /// attribute will reference the object whose content is being
117 /// received, not the content wrapper.
118 request_with_value
communicator_irecv_content(const communicator & comm,int source,int tag,content & c)119 communicator_irecv_content(const communicator& comm, int source, int tag,
120                            content& c)
121 {
122   request_with_value req(comm.irecv(source, tag, c.base()));
123   req.m_external_value = &c.object;
124   return req;
125 }
126 
127 extern const char* object_without_skeleton_docstring;
128 extern const char* object_without_skeleton_object_docstring;
129 extern const char* skeleton_proxy_docstring;
130 extern const char* skeleton_proxy_object_docstring;
131 extern const char* content_docstring;
132 extern const char* skeleton_docstring;
133 extern const char* get_content_docstring;
134 
export_skeleton_and_content(class_<communicator> & comm)135 void export_skeleton_and_content(class_<communicator>& comm)
136 {
137   using boost::python::arg;
138 
139   // Expose the object_without_skeleton exception
140   object type =
141     class_<object_without_skeleton>
142       ("ObjectWithoutSkeleton", object_without_skeleton_docstring, no_init)
143       .def_readonly("object", &object_without_skeleton::value,
144                     object_without_skeleton_object_docstring)
145       .def("__str__", &object_without_skeleton_str)
146     ;
147   translate_exception<object_without_skeleton>::declare(type);
148 
149   // Expose the Python variants of "skeleton_proxy" and "content", and
150   // their generator functions.
151   detail::skeleton_proxy_base_type =
152     class_<skeleton_proxy_base>("SkeletonProxy", skeleton_proxy_docstring,
153                                 no_init)
154       .def_readonly("object", &skeleton_proxy_base::object,
155                     skeleton_proxy_object_docstring);
156   class_<content>("Content", content_docstring, no_init);
157   def("skeleton", &skeleton, arg("object"), skeleton_docstring);
158   def("get_content", &get_content, arg("object"), get_content_docstring);
159 
160   // Expose communicator send/recv operations for content.
161   comm
162     .def("send", communicator_send_content,
163          (arg("dest"), arg("tag") = 0, arg("value")))
164     .def("recv", communicator_recv_content,
165          (arg("source") = any_source, arg("tag") = any_tag, arg("buffer"),
166           arg("return_status") = false))
167     .def("irecv", communicator_irecv_content,
168          (arg("source") = any_source, arg("tag") = any_tag, arg("buffer")),
169          with_custodian_and_ward_postcall<0, 4>()
170          );
171 }
172 
173 } } } // end namespace boost::mpi::python
174