1 // Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>
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)
7 // Authors: Douglas Gregor
9 /** @file serialize.hpp
11 * This file provides Boost.Serialization support for Python objects
12 * within Boost.MPI. Python objects can be serialized in one of two
13 * ways. The default serialization method involves using the Python
14 * "pickle" module to pickle the Python objects, transmits the
15 * pickled representation, and unpickles the result when
16 * received. For C++ types that have been exposed to Python and
17 * registered with register_serialized(), objects are directly
18 * serialized for transmissing, skipping the pickling step.
20 #ifndef BOOST_MPI_PYTHON_SERIALIZE_HPP
21 #define BOOST_MPI_PYTHON_SERIALIZE_HPP
23 #include <boost/mpi/python/config.hpp>
25 #include <boost/python/object.hpp>
26 #include <boost/python/str.hpp>
27 #include <boost/python/extract.hpp>
32 #include <boost/function/function3.hpp>
34 #include <boost/mpl/bool.hpp>
35 #include <boost/mpl/if.hpp>
37 #include <boost/serialization/split_free.hpp>
38 #include <boost/serialization/array.hpp>
40 #include <boost/assert.hpp>
42 #include <boost/type_traits/is_fundamental.hpp>
44 #define BOOST_MPI_PYTHON_FORWARD_ONLY
45 #include <boost/mpi/python.hpp>
47 /************************************************************************
48 * Boost.Python Serialization Section *
49 ************************************************************************/
50 #if !defined(BOOST_NO_SFINAE) && !defined(BOOST_NO_IS_CONVERTIBLE)
52 * @brief Declare IArchive and OArchive as a Boost.Serialization
53 * archives that can be used for Python objects.
55 * This macro can only be expanded from the global namespace. It only
56 * requires that Archiver be forward-declared. IArchiver and OArchiver
57 * will only support Serialization of Python objects by pickling
58 * them. If the Archiver type should also support "direct"
59 * serialization (for C++ types), use
60 * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE instead.
62 # define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \
63 namespace boost { namespace python { namespace api { \
64 template<typename R, typename T> \
65 struct enable_binary< IArchiver , R, T> {}; \
67 template<typename R, typename T> \
68 struct enable_binary< OArchiver , R, T> {}; \
71 # define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver)
75 * @brief Declare IArchiver and OArchiver as a Boost.Serialization
76 * archives that can be used for Python objects and C++ objects
79 * This macro can only be expanded from the global namespace. It only
80 * requires that IArchiver and OArchiver be forward-declared. However,
81 * note that you will also need to write
82 * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver,
83 * OArchiver) in one of your translation units.
87 #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \
88 BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \
89 namespace boost { namespace python { namespace detail { \
91 BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >& \
92 get_direct_serialization_table< IArchiver , OArchiver >(); \
96 struct has_direct_serialization< IArchiver , OArchiver> : mpl::true_ { }; \
99 struct output_archiver< IArchiver > { typedef OArchiver type; }; \
102 struct input_archiver< OArchiver > { typedef IArchiver type; }; \
106 * @brief Define the implementation for Boost.Serialization archivers
107 * that can be used for Python objects and C++ objects wrapped in
110 * This macro can only be expanded from the global namespace. It only
111 * requires that IArchiver and OArchiver be forward-declared. Before
112 * using this macro, you will need to declare IArchiver and OArchiver
113 * as direct serialization archives with
114 * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver).
116 #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver, OArchiver) \
117 namespace boost { namespace python { namespace detail { \
119 class BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >; \
122 BOOST_MPI_PYTHON_DECL \
123 direct_serialization_table< IArchiver , OArchiver >& \
124 get_direct_serialization_table< IArchiver , OArchiver >( ) \
126 static direct_serialization_table< IArchiver, OArchiver > table; \
131 namespace boost
{ namespace python
{
136 * Provides access to the Python "pickle" module from within C++.
138 class BOOST_MPI_PYTHON_DECL pickle
{
142 static str
dumps(object obj
, int protocol
= -1);
143 static object
loads(str s
);
146 static void initialize_data();
152 * @brief Whether the input/output archiver pair has "direct"
153 * serialization for C++ objects exposed in Python.
155 * Users do not typically need to specialize this trait, as it will be
156 * specialized as part of the macro
157 * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
159 template<typename IArchiver
, typename OArchiver
>
160 struct has_direct_serialization
: mpl::false_
{ };
163 * @brief A metafunction that determines the output archiver for the
164 * given input archiver.
166 * Users do not typically need to specialize this trait, as it will be
167 * specialized as part of the macro
168 * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
170 template<typename IArchiver
> struct output_archiver
{ };
173 * @brief A metafunction that determines the input archiver for the
174 * given output archiver.
176 * Users do not typically need to specialize this trait, as it will be
177 * specialized as part of the macro
178 * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
181 template<typename OArchiver
> struct input_archiver
{ };
188 * This class contains the direct-serialization code for the given
189 * IArchiver/OArchiver pair. It is intended to be used as a
190 * singleton class, and will be accessed when (de-)serializing a
191 * Boost.Python object with an archiver that supports direct
192 * serializations. Do not create instances of this class directly:
193 * instead, use get_direct_serialization_table.
195 template<typename IArchiver
, typename OArchiver
>
196 class BOOST_MPI_PYTHON_DECL direct_serialization_table
199 typedef boost::function3
<void, OArchiver
&, const object
&, const unsigned int>
201 typedef boost::function3
<void, IArchiver
&, object
&, const unsigned int>
204 typedef std::map
<PyTypeObject
*, std::pair
<int, saver_t
> > savers_t
;
205 typedef std::map
<int, loader_t
> loaders_t
;
208 * Retrieve the saver (serializer) associated with the Python
211 * @param obj The object we want to save. Only its (Python) type
214 * @param descriptor The value of the descriptor associated to
215 * the returned saver. Will be set to zero if no saver was found
218 * @returns a function object that can be used to serialize this
219 * object (and other objects of the same type), if possible. If
220 * no saver can be found, returns an empty function object..
222 saver_t
saver(const object
& obj
, int& descriptor
)
224 typename
savers_t::iterator pos
= savers
.find(obj
.ptr()->ob_type
);
225 if (pos
!= savers
.end()) {
226 descriptor
= pos
->second
.first
;
227 return pos
->second
.second
;
236 * Retrieve the loader (deserializer) associated with the given
239 * @param descriptor The descriptor number provided by saver()
240 * when determining the saver for this type.
242 * @returns a function object that can be used to deserialize an
243 * object whose type is the same as that corresponding to the
244 * descriptor. If the descriptor is unknown, the return value
245 * will be an empty function object.
247 loader_t
loader(int descriptor
)
249 typename
loaders_t::iterator pos
= loaders
.find(descriptor
);
250 if (pos
!= loaders
.end())
257 * Register the type T for direct serialization.
259 * @param value A sample value of the type @c T. This may be used
260 * to compute the Python type associated with the C++ type @c T.
262 * @param type The Python type associated with the C++ type @c
263 * T. If not provided, it will be computed from the same value @p
267 void register_type(const T
& value
= T(), PyTypeObject
* type
= 0)
269 // If the user did not provide us with a Python type, figure it
270 // out for ourselves.
273 type
= obj
.ptr()->ob_type
;
276 register_type(default_saver
<T
>(), default_loader
<T
>(type
), value
, type
);
280 * Register the type T for direct serialization.
282 * @param saver A function object that will serialize a
283 * Boost.Python object (that represents a C++ object of type @c
284 * T) to an @c OArchive.
286 * @param loader A function object that will deserialize from an
287 * @c IArchive into a Boost.Python object that represents a C++
288 * object of type @c T.
290 * @param value A sample value of the type @c T. This may be used
291 * to compute the Python type associated with the C++ type @c T.
293 * @param type The Python type associated with the C++ type @c
294 * T. If not provided, it will be computed from the same value @p
298 void register_type(const saver_t
& saver
, const loader_t
& loader
,
299 const T
& value
= T(), PyTypeObject
* type
= 0)
301 // If the user did not provide us with a Python type, figure it
302 // out for ourselves.
305 type
= obj
.ptr()->ob_type
;
308 int descriptor
= savers
.size() + 1;
309 if (savers
.find(type
) != savers
.end())
312 savers
[type
] = std::make_pair(descriptor
, saver
);
313 loaders
[descriptor
] = loader
;
318 struct default_saver
{
319 void operator()(OArchiver
& ar
, const object
& obj
, const unsigned int) {
320 T value
= extract
<T
>(obj
)();
326 struct default_loader
{
327 default_loader(PyTypeObject
* type
) : type(type
) { }
329 void operator()(IArchiver
& ar
, object
& obj
, const unsigned int) {
330 // If we can, extract the object in place.
331 if (!is_fundamental
<T
>::value
&& obj
&& obj
.ptr()->ob_type
== type
) {
332 ar
>> extract
<T
&>(obj
)();
349 * @brief Retrieve the direct-serialization table for an
350 * IArchiver/OArchiver pair.
352 * This function is responsible for returning a reference to the
353 * singleton direct-serialization table. Its primary template is
354 * left undefined, to force the use of an explicit specialization
355 * with a definition in a single translation unit. Use the macro
356 * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL to define this
357 * explicit specialization.
359 template<typename IArchiver
, typename OArchiver
>
360 direct_serialization_table
<IArchiver
, OArchiver
>&
361 get_direct_serialization_table();
362 } // end namespace detail
365 * @brief Register the type T for direct serialization.
367 * The @c register_serialized function registers a C++ type for direct
368 * serialization with the given @c IArchiver/@c OArchiver pair. Direct
369 * serialization elides the use of the Python @c pickle package when
370 * serializing Python objects that represent C++ values. Direct
371 * serialization can be beneficial both to improve serialization
372 * performance (Python pickling can be very inefficient) and to permit
373 * serialization for Python-wrapped C++ objects that do not support
376 * @param value A sample value of the type @c T. This may be used
377 * to compute the Python type associated with the C++ type @c T.
379 * @param type The Python type associated with the C++ type @c
380 * T. If not provided, it will be computed from the same value @p
383 template<typename IArchiver
, typename OArchiver
, typename T
>
385 register_serialized(const T
& value
= T(), PyTypeObject
* type
= 0)
387 detail::direct_serialization_table
<IArchiver
, OArchiver
>& table
=
388 detail::get_direct_serialization_table
<IArchiver
, OArchiver
>();
389 table
.register_type(value
, type
);
394 /// Save a Python object by pickling it.
395 template<typename Archiver
>
397 save_impl(Archiver
& ar
, const boost::python::object
& obj
,
398 const unsigned int /*version*/,
399 mpl::false_
/*has_direct_serialization*/)
401 boost::python::str py_string
= boost::python::pickle::dumps(obj
);
402 int len
= boost::python::extract
<int>(py_string
.attr("__len__")());
403 const char* string
= boost::python::extract
<const char*>(py_string
);
404 ar
<< len
<< boost::serialization::make_array(string
, len
);
407 /// Try to save a Python object by directly serializing it; fall back
408 /// on pickling if required.
409 template<typename Archiver
>
411 save_impl(Archiver
& ar
, const boost::python::object
& obj
,
412 const unsigned int version
,
413 mpl::true_
/*has_direct_serialization*/)
415 typedef Archiver OArchiver
;
416 typedef typename input_archiver
<OArchiver
>::type IArchiver
;
417 typedef typename direct_serialization_table
<IArchiver
, OArchiver
>::saver_t
420 direct_serialization_table
<IArchiver
, OArchiver
>& table
=
421 get_direct_serialization_table
<IArchiver
, OArchiver
>();
424 if (saver_t saver
= table
.saver(obj
, descriptor
)) {
426 saver(ar
, obj
, version
);
430 detail::save_impl(ar
, obj
, version
, mpl::false_());
434 /// Load a Python object by unpickling it
435 template<typename Archiver
>
437 load_impl(Archiver
& ar
, boost::python::object
& obj
,
438 const unsigned int /*version*/,
439 mpl::false_
/*has_direct_serialization*/)
444 std::auto_ptr
<char> string(new char[len
]);
445 ar
>> boost::serialization::make_array(string
.get(), len
);
446 boost::python::str
py_string(string
.get(), len
);
447 obj
= boost::python::pickle::loads(py_string
);
450 /// Try to load a Python object by directly deserializing it; fall back
451 /// on unpickling if required.
452 template<typename Archiver
>
454 load_impl(Archiver
& ar
, boost::python::object
& obj
,
455 const unsigned int version
,
456 mpl::true_
/*has_direct_serialization*/)
458 typedef Archiver IArchiver
;
459 typedef typename output_archiver
<IArchiver
>::type OArchiver
;
460 typedef typename direct_serialization_table
<IArchiver
, OArchiver
>::loader_t
463 direct_serialization_table
<IArchiver
, OArchiver
>& table
=
464 get_direct_serialization_table
<IArchiver
, OArchiver
>();
470 loader_t loader
= table
.loader(descriptor
);
471 BOOST_ASSERT(loader
);
473 loader(ar
, obj
, version
);
476 detail::load_impl(ar
, obj
, version
, mpl::false_());
480 } // end namespace detail
482 template<typename Archiver
>
484 save(Archiver
& ar
, const boost::python::object
& obj
,
485 const unsigned int version
)
487 typedef Archiver OArchiver
;
488 typedef typename input_archiver
<OArchiver
>::type IArchiver
;
490 detail::save_impl(ar
, obj
, version
,
491 has_direct_serialization
<IArchiver
, OArchiver
>());
494 template<typename Archiver
>
496 load(Archiver
& ar
, boost::python::object
& obj
,
497 const unsigned int version
)
499 typedef Archiver IArchiver
;
500 typedef typename output_archiver
<IArchiver
>::type OArchiver
;
502 detail::load_impl(ar
, obj
, version
,
503 has_direct_serialization
<IArchiver
, OArchiver
>());
506 template<typename Archive
>
508 serialize(Archive
& ar
, boost::python::object
& obj
, const unsigned int version
)
510 boost::serialization::split_free(ar
, obj
, version
);
513 } } // end namespace boost::python
515 /************************************************************************
516 * Boost.MPI-Specific Section *
517 ************************************************************************/
518 namespace boost
{ namespace mpi
{
519 class packed_iarchive
;
520 class packed_oarchive
;
521 } } // end namespace boost::mpi
523 BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(
524 ::boost::mpi::packed_iarchive
,
525 ::boost::mpi::packed_oarchive
)
527 namespace boost
{ namespace mpi
{ namespace python
{
531 register_serialized(const T
& value
, PyTypeObject
* type
)
533 using boost::python::register_serialized
;
534 register_serialized
<packed_iarchive
, packed_oarchive
>(value
, type
);
537 } } } // end namespace boost::mpi::python
539 #endif // BOOST_MPI_PYTHON_SERIALIZE_HPP