Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:40:57

0001 // Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>
0002 
0003 // Use, modification and distribution is subject to the Boost Software
0004 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0005 // http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 //  Authors: Douglas Gregor
0008 
0009 /** @file serialize.hpp
0010  *
0011  *  This file provides Boost.Serialization support for Python objects
0012  *  within Boost.MPI. Python objects can be serialized in one of two
0013  *  ways. The default serialization method involves using the Python
0014  *  "pickle" module to pickle the Python objects, transmits the
0015  *  pickled representation, and unpickles the result when
0016  *  received. For C++ types that have been exposed to Python and
0017  *  registered with register_serialized(), objects are directly
0018  *  serialized for transmissing, skipping the pickling step.
0019  */
0020 #ifndef BOOST_MPI_PYTHON_SERIALIZE_HPP
0021 #define BOOST_MPI_PYTHON_SERIALIZE_HPP
0022 
0023 #include <boost/mpi/python/config.hpp>
0024 
0025 #include <boost/python/object.hpp>
0026 #include <boost/python/str.hpp>
0027 #include <boost/python/extract.hpp>
0028 
0029 #include <map>
0030 
0031 #include <boost/function/function3.hpp>
0032 
0033 #include <boost/mpl/bool.hpp>
0034 #include <boost/mpl/if.hpp>
0035 
0036 #include <boost/serialization/split_free.hpp>
0037 #include <boost/serialization/array.hpp>
0038 #include <boost/serialization/array_wrapper.hpp>
0039 #include <boost/smart_ptr/scoped_array.hpp>
0040 
0041 #include <boost/assert.hpp>
0042 
0043 #include <boost/type_traits/is_fundamental.hpp>
0044 
0045 #define BOOST_MPI_PYTHON_FORWARD_ONLY
0046 #include <boost/mpi/python.hpp>
0047 
0048 #include "bytesobject.h"
0049 
0050 /************************************************************************
0051  * Boost.Python Serialization Section                                   *
0052  ************************************************************************/
0053 #if !defined(BOOST_NO_SFINAE) && !defined(BOOST_NO_IS_CONVERTIBLE)
0054 /**
0055  * @brief Declare IArchive and OArchive as a Boost.Serialization
0056  * archives that can be used for Python objects.
0057  *
0058  * This macro can only be expanded from the global namespace. It only
0059  * requires that Archiver be forward-declared. IArchiver and OArchiver
0060  * will only support Serialization of Python objects by pickling
0061  * them. If the Archiver type should also support "direct"
0062  * serialization (for C++ types), use
0063  * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE instead.
0064  */
0065 #  define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver)        \
0066 namespace boost { namespace python { namespace api {    \
0067   template<typename R, typename T>                      \
0068   struct enable_binary< IArchiver , R, T> {};           \
0069                                                         \
0070   template<typename R, typename T>                      \
0071   struct enable_binary< OArchiver , R, T> {};           \
0072 } } } 
0073 # else
0074 #  define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver)
0075 #endif
0076 
0077 /**
0078  * @brief Declare IArchiver and OArchiver as a Boost.Serialization
0079  * archives that can be used for Python objects and C++ objects
0080  * wrapped in Python.
0081  *
0082  * This macro can only be expanded from the global namespace. It only
0083  * requires that IArchiver and OArchiver be forward-declared. However,
0084  * note that you will also need to write
0085  * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver,
0086  * OArchiver) in one of your translation units.
0087 
0088 DPG PICK UP HERE
0089  */
0090 #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \
0091 BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver)                \
0092 namespace boost { namespace python { namespace detail {                 \
0093 template<>                                                              \
0094 BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >& \
0095  get_direct_serialization_table< IArchiver , OArchiver >();             \
0096 }                                                                       \
0097                                                                         \
0098 template<>                                                              \
0099 struct has_direct_serialization< IArchiver , OArchiver> : mpl::true_ { }; \
0100                                                                         \
0101 template<>                                                              \
0102 struct output_archiver< IArchiver > { typedef OArchiver type; };        \
0103                                                                         \
0104 template<>                                                              \
0105 struct input_archiver< OArchiver > { typedef IArchiver type; };         \
0106 } }
0107 
0108 /**
0109  * @brief Define the implementation for Boost.Serialization archivers
0110  * that can be used for Python objects and C++ objects wrapped in
0111  * Python.
0112  *
0113  * This macro can only be expanded from the global namespace. It only
0114  * requires that IArchiver and OArchiver be forward-declared. Before
0115  * using this macro, you will need to declare IArchiver and OArchiver
0116  * as direct serialization archives with
0117  * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver).
0118  */
0119 #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver, OArchiver) \
0120 namespace boost { namespace python { namespace detail {                 \
0121 template                                                                \
0122   class BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >; \
0123                                                                         \
0124 template<>                                                              \
0125  BOOST_MPI_PYTHON_DECL                                                  \
0126  direct_serialization_table< IArchiver , OArchiver >&                   \
0127  get_direct_serialization_table< IArchiver , OArchiver >( )             \
0128 {                                                                       \
0129   static direct_serialization_table< IArchiver, OArchiver > table;      \
0130   return table;                                                         \
0131 }                                                                       \
0132 } } }
0133 
0134 namespace boost { namespace python {
0135 
0136 /**
0137  * INTERNAL ONLY
0138  *
0139  * Provides access to the Python "pickle" module from within C++.
0140  */
0141 class BOOST_MPI_PYTHON_DECL pickle {
0142   struct data_t;
0143 
0144 public:
0145   static object dumps(object obj, int protocol = -1);
0146   static object loads(object s);
0147   
0148 private:
0149   static void initialize_data();
0150 
0151   static data_t* data;
0152 };
0153 
0154 /**
0155  * @brief Whether the input/output archiver pair has "direct"
0156  * serialization for C++ objects exposed in Python.
0157  *
0158  * Users do not typically need to specialize this trait, as it will be
0159  * specialized as part of the macro
0160  * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
0161  */
0162 template<typename IArchiver, typename OArchiver>
0163 struct has_direct_serialization : mpl::false_ { };
0164 
0165 /**
0166  *  @brief A metafunction that determines the output archiver for the
0167  *  given input archiver.
0168  *
0169  * Users do not typically need to specialize this trait, as it will be
0170  * specialized as part of the macro
0171  * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
0172  */
0173 template<typename IArchiver> struct output_archiver { };
0174 
0175 /**
0176  *  @brief A metafunction that determines the input archiver for the
0177  *  given output archiver.
0178  *
0179  * Users do not typically need to specialize this trait, as it will be
0180  * specialized as part of the macro
0181  * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
0182  *
0183  */
0184 template<typename OArchiver> struct input_archiver { };
0185 
0186 namespace detail {
0187 
0188   /**
0189    * INTERNAL ONLY
0190    *
0191    * This class contains the direct-serialization code for the given
0192    * IArchiver/OArchiver pair. It is intended to be used as a
0193    * singleton class, and will be accessed when (de-)serializing a
0194    * Boost.Python object with an archiver that supports direct
0195    * serializations. Do not create instances of this class directly:
0196    * instead, use get_direct_serialization_table.
0197    */
0198   template<typename IArchiver, typename OArchiver>
0199   class BOOST_MPI_PYTHON_DECL direct_serialization_table
0200   {
0201   public:
0202     typedef boost::function3<void, OArchiver&, const object&, const unsigned int>
0203       saver_t;
0204     typedef boost::function3<void, IArchiver&, object&, const unsigned int>
0205       loader_t;
0206 
0207     typedef std::map<PyTypeObject*, std::pair<int, saver_t> > savers_t;
0208     typedef std::map<int, loader_t> loaders_t;
0209 
0210     /**
0211      * Retrieve the saver (serializer) associated with the Python
0212      * object @p obj.
0213      *
0214      *   @param obj The object we want to save. Only its (Python) type
0215      *   is important.
0216      *
0217      *   @param descriptor The value of the descriptor associated to
0218      *   the returned saver. Will be set to zero if no saver was found
0219      *   for @p obj.
0220      *
0221      *   @returns a function object that can be used to serialize this
0222      *   object (and other objects of the same type), if possible. If
0223      *   no saver can be found, returns an empty function object..
0224      */
0225     saver_t saver(const object& obj, int& descriptor)
0226     {
0227       typename savers_t::iterator pos = savers.find(obj.ptr()->ob_type);
0228       if (pos != savers.end()) {
0229         descriptor = pos->second.first;
0230         return pos->second.second;
0231       }
0232       else {
0233         descriptor = 0;
0234         return saver_t();
0235       }
0236     }
0237 
0238     /**
0239      * Retrieve the loader (deserializer) associated with the given
0240      * descriptor.
0241      *
0242      *  @param descriptor The descriptor number provided by saver()
0243      *  when determining the saver for this type.
0244      *
0245      *  @returns a function object that can be used to deserialize an
0246      *  object whose type is the same as that corresponding to the
0247      *  descriptor. If the descriptor is unknown, the return value
0248      *  will be an empty function object.
0249      */
0250     loader_t loader(int descriptor)
0251     {
0252       typename loaders_t::iterator pos = loaders.find(descriptor);
0253       if (pos != loaders.end())
0254         return pos->second;
0255       else
0256         return loader_t();
0257     }
0258 
0259     /**
0260      * Register the type T for direct serialization.
0261      *
0262      *  @param value A sample value of the type @c T. This may be used
0263      *  to compute the Python type associated with the C++ type @c T.
0264      *
0265      *  @param type The Python type associated with the C++ type @c
0266      *  T. If not provided, it will be computed from the same value @p
0267      *  value.
0268      */
0269     template<typename T>
0270     void register_type(const T& value = T(), PyTypeObject* type = 0)
0271     {
0272       // If the user did not provide us with a Python type, figure it
0273       // out for ourselves.
0274       if (!type) {
0275         object obj(value);
0276         type = obj.ptr()->ob_type;
0277       }
0278 
0279       register_type(default_saver<T>(), default_loader<T>(type), value, type);
0280     }
0281 
0282     /**
0283      * Register the type T for direct serialization.
0284      *
0285      *  @param saver A function object that will serialize a
0286      *  Boost.Python object (that represents a C++ object of type @c
0287      *  T) to an @c OArchive.
0288      *
0289      *  @param loader A function object that will deserialize from an
0290      *  @c IArchive into a Boost.Python object that represents a C++
0291      *  object of type @c T.
0292      *
0293      *  @param value A sample value of the type @c T. This may be used
0294      *  to compute the Python type associated with the C++ type @c T.
0295      *
0296      *  @param type The Python type associated with the C++ type @c
0297      *  T. If not provided, it will be computed from the same value @p
0298      *  value.
0299      */
0300     template<typename T>
0301     void register_type(const saver_t& saver, const loader_t& loader, 
0302                        const T& value = T(), PyTypeObject* type = 0)
0303     {
0304       // If the user did not provide us with a Python type, figure it
0305       // out for ourselves.
0306       if (!type) {
0307         object obj(value);
0308         type = obj.ptr()->ob_type;
0309       }
0310 
0311       int descriptor = savers.size() + 1;
0312       if (savers.find(type) != savers.end())
0313         return;
0314 
0315       savers[type] = std::make_pair(descriptor, saver);
0316       loaders[descriptor] = loader;
0317     }
0318 
0319   protected:
0320     template<typename T>
0321     struct default_saver {
0322       void operator()(OArchiver& ar, const object& obj, const unsigned int) {
0323         T value = extract<T>(obj)();
0324         ar << value;
0325       }
0326     };
0327 
0328     template<typename T>
0329     struct default_loader {
0330       default_loader(PyTypeObject* type) : type(type) { }
0331 
0332       void operator()(IArchiver& ar, object& obj, const unsigned int) {
0333         // If we can, extract the object in place.
0334         if (!is_fundamental<T>::value && obj && obj.ptr()->ob_type == type) {
0335           ar >> extract<T&>(obj)();
0336         } else {
0337           T value;
0338           ar >> value;
0339           obj = object(value);
0340         }
0341       }
0342 
0343     private:
0344       PyTypeObject* type;
0345     };
0346 
0347     savers_t savers;
0348     loaders_t loaders;
0349   };
0350 
0351   /**
0352    * @brief Retrieve the direct-serialization table for an
0353    * IArchiver/OArchiver pair.
0354    *
0355    * This function is responsible for returning a reference to the
0356    * singleton direct-serialization table. Its primary template is
0357    * left undefined, to force the use of an explicit specialization
0358    * with a definition in a single translation unit. Use the macro
0359    * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL to define this
0360    * explicit specialization.
0361    */
0362   template<typename IArchiver, typename OArchiver>
0363   direct_serialization_table<IArchiver, OArchiver>&
0364   get_direct_serialization_table();
0365 } // end namespace detail 
0366 
0367 /**
0368  * @brief Register the type T for direct serialization.
0369  *
0370  * The @c register_serialized function registers a C++ type for direct
0371  * serialization with the given @c IArchiver/@c OArchiver pair. Direct
0372  * serialization elides the use of the Python @c pickle package when
0373  * serializing Python objects that represent C++ values. Direct
0374  * serialization can be beneficial both to improve serialization
0375  * performance (Python pickling can be very inefficient) and to permit
0376  * serialization for Python-wrapped C++ objects that do not support
0377  * pickling.
0378  *
0379  *  @param value A sample value of the type @c T. This may be used
0380  *  to compute the Python type associated with the C++ type @c T.
0381  *
0382  *  @param type The Python type associated with the C++ type @c
0383  *  T. If not provided, it will be computed from the same value @p
0384  *  value.
0385  */
0386 template<typename IArchiver, typename OArchiver, typename T>
0387 void
0388 register_serialized(const T& value = T(), PyTypeObject* type = 0)
0389 {
0390   detail::direct_serialization_table<IArchiver, OArchiver>& table = 
0391     detail::get_direct_serialization_table<IArchiver, OArchiver>();
0392   table.register_type(value, type);
0393 }
0394 
0395 namespace detail {
0396 
0397 /// Save a Python object by pickling it.
0398 template<typename Archiver>
0399 void 
0400 save_impl(Archiver& ar, const boost::python::object& obj, 
0401           const unsigned int /*version*/,
0402           mpl::false_ /*has_direct_serialization*/)
0403 {
0404   boost::python::object bytes = boost::python::pickle::dumps(obj);
0405   int   sz    = PyBytes_Size(bytes.ptr());
0406   char *data  = PyBytes_AsString(bytes.ptr());  
0407   ar << sz << boost::serialization::make_array(data, sz);
0408 }
0409 
0410 /// Try to save a Python object by directly serializing it; fall back
0411 /// on pickling if required.
0412 template<typename Archiver>
0413 void 
0414 save_impl(Archiver& ar, const boost::python::object& obj, 
0415           const unsigned int version,
0416           mpl::true_ /*has_direct_serialization*/)
0417 {
0418   typedef Archiver OArchiver;
0419   typedef typename input_archiver<OArchiver>::type IArchiver;
0420   typedef typename direct_serialization_table<IArchiver, OArchiver>::saver_t
0421     saver_t;
0422 
0423   direct_serialization_table<IArchiver, OArchiver>& table = 
0424     get_direct_serialization_table<IArchiver, OArchiver>();
0425 
0426   int descriptor = 0;
0427   if (saver_t saver = table.saver(obj, descriptor)) {
0428     ar << descriptor;
0429     saver(ar, obj, version);
0430   } else {
0431     // Pickle it
0432     ar << descriptor;
0433     detail::save_impl(ar, obj, version, mpl::false_());
0434   }
0435 }
0436 
0437 /// Load a Python object by unpickling it
0438 template<typename Archiver>
0439 void 
0440 load_impl(Archiver& ar, boost::python::object& obj, 
0441           const unsigned int /*version*/, 
0442           mpl::false_ /*has_direct_serialization*/)
0443 {
0444   int len;
0445   ar >> len;
0446   boost::scoped_array<char> data(new char[len]);
0447   ar >> boost::serialization::make_array(data.get(), len);
0448   boost::python::object bytes(boost::python::handle<>(PyBytes_FromStringAndSize(data.get(), len)));
0449   obj = boost::python::pickle::loads(bytes);
0450 }
0451 
0452 /// Try to load a Python object by directly deserializing it; fall back
0453 /// on unpickling if required.
0454 template<typename Archiver>
0455 void 
0456 load_impl(Archiver& ar, boost::python::object& obj, 
0457           const unsigned int version,
0458           mpl::true_ /*has_direct_serialization*/)
0459 {
0460   typedef Archiver IArchiver;
0461   typedef typename output_archiver<IArchiver>::type OArchiver;
0462   typedef typename direct_serialization_table<IArchiver, OArchiver>::loader_t
0463     loader_t;
0464 
0465   direct_serialization_table<IArchiver, OArchiver>& table = 
0466     get_direct_serialization_table<IArchiver, OArchiver>();
0467 
0468   int descriptor;
0469   ar >> descriptor;
0470 
0471   if (descriptor) {
0472     loader_t loader = table.loader(descriptor);
0473     BOOST_ASSERT(loader);
0474 
0475     loader(ar, obj, version);
0476   } else {
0477     // Unpickle it
0478     detail::load_impl(ar, obj, version, mpl::false_());
0479   }
0480 }
0481 
0482 } // end namespace detail
0483 
0484 template<typename Archiver>
0485 void 
0486 save(Archiver& ar, const boost::python::object& obj, 
0487      const unsigned int version)
0488 {
0489   typedef Archiver OArchiver;
0490   typedef typename input_archiver<OArchiver>::type IArchiver;
0491   detail::save_impl(ar, obj, version, 
0492                     has_direct_serialization<IArchiver, OArchiver>());
0493 }
0494 
0495 template<typename Archiver>
0496 void 
0497 load(Archiver& ar, boost::python::object& obj, 
0498      const unsigned int version)
0499 {
0500   typedef Archiver IArchiver;
0501   typedef typename output_archiver<IArchiver>::type OArchiver;
0502   detail::load_impl(ar, obj, version, 
0503                     has_direct_serialization<IArchiver, OArchiver>());
0504 }
0505 
0506 template<typename Archive>
0507 inline void 
0508 serialize(Archive& ar, boost::python::object& obj, const unsigned int version)
0509 {
0510   boost::serialization::split_free(ar, obj, version);
0511 }
0512 
0513 } } // end namespace boost::python
0514 
0515 /************************************************************************
0516  * Boost.MPI-Specific Section                                           *
0517  ************************************************************************/
0518 namespace boost { namespace mpi {
0519  class packed_iarchive;
0520  class packed_oarchive;
0521 } } // end namespace boost::mpi
0522 
0523 BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(
0524   ::boost::mpi::packed_iarchive,
0525   ::boost::mpi::packed_oarchive)
0526 
0527 namespace boost { namespace mpi { namespace python {
0528 
0529 template<typename T>
0530 void
0531 register_serialized(const T& value, PyTypeObject* type)
0532 {
0533   using boost::python::register_serialized;
0534   register_serialized<packed_iarchive, packed_oarchive>(value, type);
0535 }
0536 
0537 } } } // end namespace boost::mpi::python
0538 
0539 #endif // BOOST_MPI_PYTHON_SERIALIZE_HPP