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