summaryrefslogtreecommitdiff
path: root/libs/mpi/src/python/skeleton_and_content.cpp
blob: d5376c14fbf73dc0f626cfd5802f0db2d4a0d6cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// (C) Copyright 2006 Douglas Gregor <doug.gregor -at- gmail.com>

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  Authors: Douglas Gregor

/** @file skeleton_and_content.cpp
 *
 *  This file reflects the skeleton/content facilities into Python.
 */
#include <boost/mpi/python/skeleton_and_content.hpp>
#include <boost/mpi/python/serialize.hpp>
#include <boost/python/list.hpp>
#include <typeinfo>
#include <list>
#include "utility.hpp"
#include "request_with_value.hpp"

using namespace boost::python;
using namespace boost::mpi;

namespace boost { namespace mpi { namespace python {

namespace detail {
  typedef std::map<PyTypeObject*, skeleton_content_handler>
    skeleton_content_handlers_type;

// We're actually importing skeleton_content_handlers from skeleton_and_content.cpp.
#if defined(BOOST_HAS_DECLSPEC) && (defined(BOOST_MPI_PYTHON_DYN_LINK) || defined(BOOST_ALL_DYN_LINK))
#  define BOOST_SC_DECL __declspec(dllimport)
#else
#  define BOOST_SC_DECL
#endif

  extern BOOST_SC_DECL skeleton_content_handlers_type skeleton_content_handlers;
}

/**
 * An exception that will be thrown when the object passed to the
 * Python version of skeleton() does not have a skeleton.
 */
struct object_without_skeleton : public std::exception {
  explicit object_without_skeleton(object value) : value(value) { }
  virtual ~object_without_skeleton() throw() { }

  object value;
};

str object_without_skeleton_str(const object_without_skeleton& e)
{
  return str("\nThe skeleton() or get_content() function was invoked for a Python\n"
             "object that is not supported by the Boost.MPI skeleton/content\n"
             "mechanism. To transfer objects via skeleton/content, you must\n"
             "register the C++ type of this object with the C++ function:\n"
             "  boost::mpi::python::register_skeleton_and_content()\n"
             "Object: " + str(e.value) + "\n");
}

/**
 * Extract the "skeleton" from a Python object. In truth, all we're
 * doing at this point is verifying that the object is a C++ type that
 * has been registered for the skeleton/content mechanism.
 */
object skeleton(object value)
{
  PyTypeObject* type = value.ptr()->ob_type;
  detail::skeleton_content_handlers_type::iterator pos = 
    detail::skeleton_content_handlers.find(type);
  if (pos == detail::skeleton_content_handlers.end())
    throw object_without_skeleton(value);
  else
    return pos->second.get_skeleton_proxy(value);
}

/**
 * Extract the "content" from a Python object, which must be a C++
 * type that has been registered for the skeleton/content mechanism.
 */
content get_content(object value)
{
  PyTypeObject* type = value.ptr()->ob_type;
  detail::skeleton_content_handlers_type::iterator pos = 
    detail::skeleton_content_handlers.find(type);
  if (pos == detail::skeleton_content_handlers.end())
    throw object_without_skeleton(value);
  else
    return pos->second.get_content(value);
}

/// Send the content part of a Python object.
void 
communicator_send_content(const communicator& comm, int dest, int tag, 
                          const content& c)
{
  comm.send(dest, tag, c.base());
}

/// Receive the content of a Python object. We return the object
/// received, not the content wrapper.
object 
communicator_recv_content(const communicator& comm, int source, int tag,
                          const content& c, bool return_status)
{
  using boost::python::make_tuple;

  status stat = comm.recv(source, tag, c.base());
  if (return_status)
    return make_tuple(c.object, stat);
  else
    return c.object;
}

/// Receive the content of a Python object. The request object's value
/// attribute will reference the object whose content is being
/// received, not the content wrapper.
request_with_value
communicator_irecv_content(const communicator& comm, int source, int tag,
                           content& c)
{
  request_with_value req(comm.irecv(source, tag, c.base()));
  req.m_external_value = &c.object;
  return req;
}

extern const char* object_without_skeleton_docstring;
extern const char* object_without_skeleton_object_docstring;
extern const char* skeleton_proxy_docstring;
extern const char* skeleton_proxy_object_docstring;
extern const char* content_docstring;
extern const char* skeleton_docstring;
extern const char* get_content_docstring;

void export_skeleton_and_content(class_<communicator>& comm)
{
  using boost::python::arg;

  // Expose the object_without_skeleton exception
  object type = 
    class_<object_without_skeleton>
      ("ObjectWithoutSkeleton", object_without_skeleton_docstring, no_init)
      .def_readonly("object", &object_without_skeleton::value,
                    object_without_skeleton_object_docstring)
      .def("__str__", &object_without_skeleton_str)
    ;
  translate_exception<object_without_skeleton>::declare(type);

  // Expose the Python variants of "skeleton_proxy" and "content", and
  // their generator functions.
  detail::skeleton_proxy_base_type = 
    class_<skeleton_proxy_base>("SkeletonProxy", skeleton_proxy_docstring, 
                                no_init)
      .def_readonly("object", &skeleton_proxy_base::object,
                    skeleton_proxy_object_docstring);
  class_<content>("Content", content_docstring, no_init);
  def("skeleton", &skeleton, arg("object"), skeleton_docstring);
  def("get_content", &get_content, arg("object"), get_content_docstring);

  // Expose communicator send/recv operations for content.
  comm
    .def("send", communicator_send_content,
         (arg("dest"), arg("tag") = 0, arg("value")))
    .def("recv", communicator_recv_content,
         (arg("source") = any_source, arg("tag") = any_tag, arg("buffer"), 
          arg("return_status") = false))
    .def("irecv", communicator_irecv_content,
         (arg("source") = any_source, arg("tag") = any_tag, arg("buffer")),
         with_custodian_and_ward_postcall<0, 4>()
         );
}

} } } // end namespace boost::mpi::python