Chris@16: // Copyright (C) 2006 Douglas Gregor Chris@16: Chris@16: // Use, modification and distribution is subject to the Boost Software Chris@16: // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // Authors: Douglas Gregor Chris@16: Chris@16: /** @file serialize.hpp Chris@16: * Chris@16: * This file provides Boost.Serialization support for Python objects Chris@16: * within Boost.MPI. Python objects can be serialized in one of two Chris@16: * ways. The default serialization method involves using the Python Chris@16: * "pickle" module to pickle the Python objects, transmits the Chris@16: * pickled representation, and unpickles the result when Chris@16: * received. For C++ types that have been exposed to Python and Chris@16: * registered with register_serialized(), objects are directly Chris@16: * serialized for transmissing, skipping the pickling step. Chris@16: */ Chris@16: #ifndef BOOST_MPI_PYTHON_SERIALIZE_HPP Chris@16: #define BOOST_MPI_PYTHON_SERIALIZE_HPP Chris@16: Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: #define BOOST_MPI_PYTHON_FORWARD_ONLY Chris@16: #include Chris@16: Chris@16: /************************************************************************ Chris@16: * Boost.Python Serialization Section * Chris@16: ************************************************************************/ Chris@16: #if !defined(BOOST_NO_SFINAE) && !defined(BOOST_NO_IS_CONVERTIBLE) Chris@16: /** Chris@16: * @brief Declare IArchive and OArchive as a Boost.Serialization Chris@16: * archives that can be used for Python objects. Chris@16: * Chris@16: * This macro can only be expanded from the global namespace. It only Chris@16: * requires that Archiver be forward-declared. IArchiver and OArchiver Chris@16: * will only support Serialization of Python objects by pickling Chris@16: * them. If the Archiver type should also support "direct" Chris@16: * serialization (for C++ types), use Chris@16: * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE instead. Chris@16: */ Chris@16: # define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \ Chris@16: namespace boost { namespace python { namespace api { \ Chris@16: template \ Chris@16: struct enable_binary< IArchiver , R, T> {}; \ Chris@16: \ Chris@16: template \ Chris@16: struct enable_binary< OArchiver , R, T> {}; \ Chris@16: } } } Chris@16: # else Chris@16: # define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) Chris@16: #endif Chris@16: Chris@16: /** Chris@16: * @brief Declare IArchiver and OArchiver as a Boost.Serialization Chris@16: * archives that can be used for Python objects and C++ objects Chris@16: * wrapped in Python. Chris@16: * Chris@16: * This macro can only be expanded from the global namespace. It only Chris@16: * requires that IArchiver and OArchiver be forward-declared. However, Chris@16: * note that you will also need to write Chris@16: * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver, Chris@16: * OArchiver) in one of your translation units. Chris@16: Chris@16: DPG PICK UP HERE Chris@16: */ Chris@16: #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \ Chris@16: BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \ Chris@16: namespace boost { namespace python { namespace detail { \ Chris@16: template<> \ Chris@16: BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >& \ Chris@16: get_direct_serialization_table< IArchiver , OArchiver >(); \ Chris@16: } \ Chris@16: \ Chris@16: template<> \ Chris@16: struct has_direct_serialization< IArchiver , OArchiver> : mpl::true_ { }; \ Chris@16: \ Chris@16: template<> \ Chris@16: struct output_archiver< IArchiver > { typedef OArchiver type; }; \ Chris@16: \ Chris@16: template<> \ Chris@16: struct input_archiver< OArchiver > { typedef IArchiver type; }; \ Chris@16: } } Chris@16: Chris@16: /** Chris@16: * @brief Define the implementation for Boost.Serialization archivers Chris@16: * that can be used for Python objects and C++ objects wrapped in Chris@16: * Python. Chris@16: * Chris@16: * This macro can only be expanded from the global namespace. It only Chris@16: * requires that IArchiver and OArchiver be forward-declared. Before Chris@16: * using this macro, you will need to declare IArchiver and OArchiver Chris@16: * as direct serialization archives with Chris@16: * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver). Chris@16: */ Chris@16: #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver, OArchiver) \ Chris@16: namespace boost { namespace python { namespace detail { \ Chris@16: template \ Chris@16: class BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >; \ Chris@16: \ Chris@16: template<> \ Chris@16: BOOST_MPI_PYTHON_DECL \ Chris@16: direct_serialization_table< IArchiver , OArchiver >& \ Chris@16: get_direct_serialization_table< IArchiver , OArchiver >( ) \ Chris@16: { \ Chris@16: static direct_serialization_table< IArchiver, OArchiver > table; \ Chris@16: return table; \ Chris@16: } \ Chris@16: } } } Chris@16: Chris@16: namespace boost { namespace python { Chris@16: Chris@16: /** Chris@16: * INTERNAL ONLY Chris@16: * Chris@16: * Provides access to the Python "pickle" module from within C++. Chris@16: */ Chris@16: class BOOST_MPI_PYTHON_DECL pickle { Chris@16: struct data_t; Chris@16: Chris@16: public: Chris@16: static str dumps(object obj, int protocol = -1); Chris@16: static object loads(str s); Chris@16: Chris@16: private: Chris@16: static void initialize_data(); Chris@16: Chris@16: static data_t* data; Chris@16: }; Chris@16: Chris@16: /** Chris@16: * @brief Whether the input/output archiver pair has "direct" Chris@16: * serialization for C++ objects exposed in Python. Chris@16: * Chris@16: * Users do not typically need to specialize this trait, as it will be Chris@16: * specialized as part of the macro Chris@16: * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE. Chris@16: */ Chris@16: template Chris@16: struct has_direct_serialization : mpl::false_ { }; Chris@16: Chris@16: /** Chris@16: * @brief A metafunction that determines the output archiver for the Chris@16: * given input archiver. Chris@16: * Chris@16: * Users do not typically need to specialize this trait, as it will be Chris@16: * specialized as part of the macro Chris@16: * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE. Chris@16: */ Chris@16: template struct output_archiver { }; Chris@16: Chris@16: /** Chris@16: * @brief A metafunction that determines the input archiver for the Chris@16: * given output archiver. Chris@16: * Chris@16: * Users do not typically need to specialize this trait, as it will be Chris@16: * specialized as part of the macro Chris@16: * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE. Chris@16: * Chris@16: */ Chris@16: template struct input_archiver { }; Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: /** Chris@16: * INTERNAL ONLY Chris@16: * Chris@16: * This class contains the direct-serialization code for the given Chris@16: * IArchiver/OArchiver pair. It is intended to be used as a Chris@16: * singleton class, and will be accessed when (de-)serializing a Chris@16: * Boost.Python object with an archiver that supports direct Chris@16: * serializations. Do not create instances of this class directly: Chris@16: * instead, use get_direct_serialization_table. Chris@16: */ Chris@16: template Chris@16: class BOOST_MPI_PYTHON_DECL direct_serialization_table Chris@16: { Chris@16: public: Chris@16: typedef boost::function3 Chris@16: saver_t; Chris@16: typedef boost::function3 Chris@16: loader_t; Chris@16: Chris@16: typedef std::map > savers_t; Chris@16: typedef std::map loaders_t; Chris@16: Chris@16: /** Chris@16: * Retrieve the saver (serializer) associated with the Python Chris@16: * object @p obj. Chris@16: * Chris@16: * @param obj The object we want to save. Only its (Python) type Chris@16: * is important. Chris@16: * Chris@16: * @param descriptor The value of the descriptor associated to Chris@16: * the returned saver. Will be set to zero if no saver was found Chris@16: * for @p obj. Chris@16: * Chris@16: * @returns a function object that can be used to serialize this Chris@16: * object (and other objects of the same type), if possible. If Chris@16: * no saver can be found, returns an empty function object.. Chris@16: */ Chris@16: saver_t saver(const object& obj, int& descriptor) Chris@16: { Chris@16: typename savers_t::iterator pos = savers.find(obj.ptr()->ob_type); Chris@16: if (pos != savers.end()) { Chris@16: descriptor = pos->second.first; Chris@16: return pos->second.second; Chris@16: } Chris@16: else { Chris@16: descriptor = 0; Chris@16: return saver_t(); Chris@16: } Chris@16: } Chris@16: Chris@16: /** Chris@16: * Retrieve the loader (deserializer) associated with the given Chris@16: * descriptor. Chris@16: * Chris@16: * @param descriptor The descriptor number provided by saver() Chris@16: * when determining the saver for this type. Chris@16: * Chris@16: * @returns a function object that can be used to deserialize an Chris@16: * object whose type is the same as that corresponding to the Chris@16: * descriptor. If the descriptor is unknown, the return value Chris@16: * will be an empty function object. Chris@16: */ Chris@16: loader_t loader(int descriptor) Chris@16: { Chris@16: typename loaders_t::iterator pos = loaders.find(descriptor); Chris@16: if (pos != loaders.end()) Chris@16: return pos->second; Chris@16: else Chris@16: return loader_t(); Chris@16: } Chris@16: Chris@16: /** Chris@16: * Register the type T for direct serialization. Chris@16: * Chris@16: * @param value A sample value of the type @c T. This may be used Chris@16: * to compute the Python type associated with the C++ type @c T. Chris@16: * Chris@16: * @param type The Python type associated with the C++ type @c Chris@16: * T. If not provided, it will be computed from the same value @p Chris@16: * value. Chris@16: */ Chris@16: template Chris@16: void register_type(const T& value = T(), PyTypeObject* type = 0) Chris@16: { Chris@16: // If the user did not provide us with a Python type, figure it Chris@16: // out for ourselves. Chris@16: if (!type) { Chris@16: object obj(value); Chris@16: type = obj.ptr()->ob_type; Chris@16: } Chris@16: Chris@16: register_type(default_saver(), default_loader(type), value, type); Chris@16: } Chris@16: Chris@16: /** Chris@16: * Register the type T for direct serialization. Chris@16: * Chris@16: * @param saver A function object that will serialize a Chris@16: * Boost.Python object (that represents a C++ object of type @c Chris@16: * T) to an @c OArchive. Chris@16: * Chris@16: * @param loader A function object that will deserialize from an Chris@16: * @c IArchive into a Boost.Python object that represents a C++ Chris@16: * object of type @c T. Chris@16: * Chris@16: * @param value A sample value of the type @c T. This may be used Chris@16: * to compute the Python type associated with the C++ type @c T. Chris@16: * Chris@16: * @param type The Python type associated with the C++ type @c Chris@16: * T. If not provided, it will be computed from the same value @p Chris@16: * value. Chris@16: */ Chris@16: template Chris@16: void register_type(const saver_t& saver, const loader_t& loader, Chris@16: const T& value = T(), PyTypeObject* type = 0) Chris@16: { Chris@16: // If the user did not provide us with a Python type, figure it Chris@16: // out for ourselves. Chris@16: if (!type) { Chris@16: object obj(value); Chris@16: type = obj.ptr()->ob_type; Chris@16: } Chris@16: Chris@16: int descriptor = savers.size() + 1; Chris@16: if (savers.find(type) != savers.end()) Chris@16: return; Chris@16: Chris@16: savers[type] = std::make_pair(descriptor, saver); Chris@16: loaders[descriptor] = loader; Chris@16: } Chris@16: Chris@16: protected: Chris@16: template Chris@16: struct default_saver { Chris@16: void operator()(OArchiver& ar, const object& obj, const unsigned int) { Chris@16: T value = extract(obj)(); Chris@16: ar << value; Chris@16: } Chris@16: }; Chris@16: Chris@16: template Chris@16: struct default_loader { Chris@16: default_loader(PyTypeObject* type) : type(type) { } Chris@16: Chris@16: void operator()(IArchiver& ar, object& obj, const unsigned int) { Chris@16: // If we can, extract the object in place. Chris@16: if (!is_fundamental::value && obj && obj.ptr()->ob_type == type) { Chris@16: ar >> extract(obj)(); Chris@16: } else { Chris@16: T value; Chris@16: ar >> value; Chris@16: obj = object(value); Chris@16: } Chris@16: } Chris@16: Chris@16: private: Chris@16: PyTypeObject* type; Chris@16: }; Chris@16: Chris@16: savers_t savers; Chris@16: loaders_t loaders; Chris@16: }; Chris@16: Chris@16: /** Chris@16: * @brief Retrieve the direct-serialization table for an Chris@16: * IArchiver/OArchiver pair. Chris@16: * Chris@16: * This function is responsible for returning a reference to the Chris@16: * singleton direct-serialization table. Its primary template is Chris@16: * left undefined, to force the use of an explicit specialization Chris@16: * with a definition in a single translation unit. Use the macro Chris@16: * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL to define this Chris@16: * explicit specialization. Chris@16: */ Chris@16: template Chris@16: direct_serialization_table& Chris@16: get_direct_serialization_table(); Chris@16: } // end namespace detail Chris@16: Chris@16: /** Chris@16: * @brief Register the type T for direct serialization. Chris@16: * Chris@16: * The @c register_serialized function registers a C++ type for direct Chris@16: * serialization with the given @c IArchiver/@c OArchiver pair. Direct Chris@16: * serialization elides the use of the Python @c pickle package when Chris@16: * serializing Python objects that represent C++ values. Direct Chris@16: * serialization can be beneficial both to improve serialization Chris@16: * performance (Python pickling can be very inefficient) and to permit Chris@16: * serialization for Python-wrapped C++ objects that do not support Chris@16: * pickling. Chris@16: * Chris@16: * @param value A sample value of the type @c T. This may be used Chris@16: * to compute the Python type associated with the C++ type @c T. Chris@16: * Chris@16: * @param type The Python type associated with the C++ type @c Chris@16: * T. If not provided, it will be computed from the same value @p Chris@16: * value. Chris@16: */ Chris@16: template Chris@16: void Chris@16: register_serialized(const T& value = T(), PyTypeObject* type = 0) Chris@16: { Chris@16: detail::direct_serialization_table& table = Chris@16: detail::get_direct_serialization_table(); Chris@16: table.register_type(value, type); Chris@16: } Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: /// Save a Python object by pickling it. Chris@16: template Chris@16: void Chris@16: save_impl(Archiver& ar, const boost::python::object& obj, Chris@16: const unsigned int /*version*/, Chris@16: mpl::false_ /*has_direct_serialization*/) Chris@16: { Chris@16: boost::python::str py_string = boost::python::pickle::dumps(obj); Chris@16: int len = boost::python::extract(py_string.attr("__len__")()); Chris@16: const char* string = boost::python::extract(py_string); Chris@16: ar << len << boost::serialization::make_array(string, len); Chris@16: } Chris@16: Chris@16: /// Try to save a Python object by directly serializing it; fall back Chris@16: /// on pickling if required. Chris@16: template Chris@16: void Chris@16: save_impl(Archiver& ar, const boost::python::object& obj, Chris@16: const unsigned int version, Chris@16: mpl::true_ /*has_direct_serialization*/) Chris@16: { Chris@16: typedef Archiver OArchiver; Chris@16: typedef typename input_archiver::type IArchiver; Chris@16: typedef typename direct_serialization_table::saver_t Chris@16: saver_t; Chris@16: Chris@16: direct_serialization_table& table = Chris@16: get_direct_serialization_table(); Chris@16: Chris@16: int descriptor = 0; Chris@16: if (saver_t saver = table.saver(obj, descriptor)) { Chris@16: ar << descriptor; Chris@16: saver(ar, obj, version); Chris@16: } else { Chris@16: // Pickle it Chris@16: ar << descriptor; Chris@16: detail::save_impl(ar, obj, version, mpl::false_()); Chris@16: } Chris@16: } Chris@16: Chris@16: /// Load a Python object by unpickling it Chris@16: template Chris@16: void Chris@16: load_impl(Archiver& ar, boost::python::object& obj, Chris@16: const unsigned int /*version*/, Chris@16: mpl::false_ /*has_direct_serialization*/) Chris@16: { Chris@16: int len; Chris@16: ar >> len; Chris@16: Chris@16: std::auto_ptr string(new char[len]); Chris@16: ar >> boost::serialization::make_array(string.get(), len); Chris@16: boost::python::str py_string(string.get(), len); Chris@16: obj = boost::python::pickle::loads(py_string); Chris@16: } Chris@16: Chris@16: /// Try to load a Python object by directly deserializing it; fall back Chris@16: /// on unpickling if required. Chris@16: template Chris@16: void Chris@16: load_impl(Archiver& ar, boost::python::object& obj, Chris@16: const unsigned int version, Chris@16: mpl::true_ /*has_direct_serialization*/) Chris@16: { Chris@16: typedef Archiver IArchiver; Chris@16: typedef typename output_archiver::type OArchiver; Chris@16: typedef typename direct_serialization_table::loader_t Chris@16: loader_t; Chris@16: Chris@16: direct_serialization_table& table = Chris@16: get_direct_serialization_table(); Chris@16: Chris@16: int descriptor; Chris@16: ar >> descriptor; Chris@16: Chris@16: if (descriptor) { Chris@16: loader_t loader = table.loader(descriptor); Chris@16: BOOST_ASSERT(loader); Chris@16: Chris@16: loader(ar, obj, version); Chris@16: } else { Chris@16: // Unpickle it Chris@16: detail::load_impl(ar, obj, version, mpl::false_()); Chris@16: } Chris@16: } Chris@16: Chris@16: } // end namespace detail Chris@16: Chris@16: template Chris@16: void Chris@16: save(Archiver& ar, const boost::python::object& obj, Chris@16: const unsigned int version) Chris@16: { Chris@16: typedef Archiver OArchiver; Chris@16: typedef typename input_archiver::type IArchiver; Chris@16: Chris@16: detail::save_impl(ar, obj, version, Chris@16: has_direct_serialization()); Chris@16: } Chris@16: Chris@16: template Chris@16: void Chris@16: load(Archiver& ar, boost::python::object& obj, Chris@16: const unsigned int version) Chris@16: { Chris@16: typedef Archiver IArchiver; Chris@16: typedef typename output_archiver::type OArchiver; Chris@16: Chris@16: detail::load_impl(ar, obj, version, Chris@16: has_direct_serialization()); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void Chris@16: serialize(Archive& ar, boost::python::object& obj, const unsigned int version) Chris@16: { Chris@16: boost::serialization::split_free(ar, obj, version); Chris@16: } Chris@16: Chris@16: } } // end namespace boost::python Chris@16: Chris@16: /************************************************************************ Chris@16: * Boost.MPI-Specific Section * Chris@16: ************************************************************************/ Chris@16: namespace boost { namespace mpi { Chris@16: class packed_iarchive; Chris@16: class packed_oarchive; Chris@16: } } // end namespace boost::mpi Chris@16: Chris@16: BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE( Chris@16: ::boost::mpi::packed_iarchive, Chris@16: ::boost::mpi::packed_oarchive) Chris@16: Chris@16: namespace boost { namespace mpi { namespace python { Chris@16: Chris@16: template Chris@16: void Chris@16: register_serialized(const T& value, PyTypeObject* type) Chris@16: { Chris@16: using boost::python::register_serialized; Chris@16: register_serialized(value, type); Chris@16: } Chris@16: Chris@16: } } } // end namespace boost::mpi::python Chris@16: Chris@16: #endif // BOOST_MPI_PYTHON_SERIALIZE_HPP