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