serialize.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. // Copyright (C) 2006 Douglas Gregor <doug.gregor -at- gmail.com>
  2. // Use, modification and distribution is subject to the Boost Software
  3. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. // Authors: Douglas Gregor
  6. /** @file serialize.hpp
  7. *
  8. * This file provides Boost.Serialization support for Python objects
  9. * within Boost.MPI. Python objects can be serialized in one of two
  10. * ways. The default serialization method involves using the Python
  11. * "pickle" module to pickle the Python objects, transmits the
  12. * pickled representation, and unpickles the result when
  13. * received. For C++ types that have been exposed to Python and
  14. * registered with register_serialized(), objects are directly
  15. * serialized for transmissing, skipping the pickling step.
  16. */
  17. #ifndef BOOST_MPI_PYTHON_SERIALIZE_HPP
  18. #define BOOST_MPI_PYTHON_SERIALIZE_HPP
  19. #include <boost/mpi/python/config.hpp>
  20. #include <boost/python/object.hpp>
  21. #include <boost/python/str.hpp>
  22. #include <boost/python/extract.hpp>
  23. #include <map>
  24. #include <boost/function/function3.hpp>
  25. #include <boost/mpl/bool.hpp>
  26. #include <boost/mpl/if.hpp>
  27. #include <boost/serialization/split_free.hpp>
  28. #include <boost/serialization/array.hpp>
  29. #include <boost/serialization/array_wrapper.hpp>
  30. #include <boost/smart_ptr/scoped_array.hpp>
  31. #include <boost/assert.hpp>
  32. #include <boost/type_traits/is_fundamental.hpp>
  33. #define BOOST_MPI_PYTHON_FORWARD_ONLY
  34. #include <boost/mpi/python.hpp>
  35. #include "bytesobject.h"
  36. /************************************************************************
  37. * Boost.Python Serialization Section *
  38. ************************************************************************/
  39. #if !defined(BOOST_NO_SFINAE) && !defined(BOOST_NO_IS_CONVERTIBLE)
  40. /**
  41. * @brief Declare IArchive and OArchive as a Boost.Serialization
  42. * archives that can be used for Python objects.
  43. *
  44. * This macro can only be expanded from the global namespace. It only
  45. * requires that Archiver be forward-declared. IArchiver and OArchiver
  46. * will only support Serialization of Python objects by pickling
  47. * them. If the Archiver type should also support "direct"
  48. * serialization (for C++ types), use
  49. * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE instead.
  50. */
  51. # define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \
  52. namespace boost { namespace python { namespace api { \
  53. template<typename R, typename T> \
  54. struct enable_binary< IArchiver , R, T> {}; \
  55. \
  56. template<typename R, typename T> \
  57. struct enable_binary< OArchiver , R, T> {}; \
  58. } } }
  59. # else
  60. # define BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver)
  61. #endif
  62. /**
  63. * @brief Declare IArchiver and OArchiver as a Boost.Serialization
  64. * archives that can be used for Python objects and C++ objects
  65. * wrapped in Python.
  66. *
  67. * This macro can only be expanded from the global namespace. It only
  68. * requires that IArchiver and OArchiver be forward-declared. However,
  69. * note that you will also need to write
  70. * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver,
  71. * OArchiver) in one of your translation units.
  72. DPG PICK UP HERE
  73. */
  74. #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \
  75. BOOST_PYTHON_SERIALIZATION_ARCHIVE(IArchiver, OArchiver) \
  76. namespace boost { namespace python { namespace detail { \
  77. template<> \
  78. BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >& \
  79. get_direct_serialization_table< IArchiver , OArchiver >(); \
  80. } \
  81. \
  82. template<> \
  83. struct has_direct_serialization< IArchiver , OArchiver> : mpl::true_ { }; \
  84. \
  85. template<> \
  86. struct output_archiver< IArchiver > { typedef OArchiver type; }; \
  87. \
  88. template<> \
  89. struct input_archiver< OArchiver > { typedef IArchiver type; }; \
  90. } }
  91. /**
  92. * @brief Define the implementation for Boost.Serialization archivers
  93. * that can be used for Python objects and C++ objects wrapped in
  94. * Python.
  95. *
  96. * This macro can only be expanded from the global namespace. It only
  97. * requires that IArchiver and OArchiver be forward-declared. Before
  98. * using this macro, you will need to declare IArchiver and OArchiver
  99. * as direct serialization archives with
  100. * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(IArchiver, OArchiver).
  101. */
  102. #define BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL(IArchiver, OArchiver) \
  103. namespace boost { namespace python { namespace detail { \
  104. template \
  105. class BOOST_MPI_PYTHON_DECL direct_serialization_table< IArchiver , OArchiver >; \
  106. \
  107. template<> \
  108. BOOST_MPI_PYTHON_DECL \
  109. direct_serialization_table< IArchiver , OArchiver >& \
  110. get_direct_serialization_table< IArchiver , OArchiver >( ) \
  111. { \
  112. static direct_serialization_table< IArchiver, OArchiver > table; \
  113. return table; \
  114. } \
  115. } } }
  116. namespace boost { namespace python {
  117. /**
  118. * INTERNAL ONLY
  119. *
  120. * Provides access to the Python "pickle" module from within C++.
  121. */
  122. class BOOST_MPI_PYTHON_DECL pickle {
  123. struct data_t;
  124. public:
  125. static object dumps(object obj, int protocol = -1);
  126. static object loads(object s);
  127. private:
  128. static void initialize_data();
  129. static data_t* data;
  130. };
  131. /**
  132. * @brief Whether the input/output archiver pair has "direct"
  133. * serialization for C++ objects exposed in Python.
  134. *
  135. * Users do not typically need to specialize this trait, as it will be
  136. * specialized as part of the macro
  137. * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
  138. */
  139. template<typename IArchiver, typename OArchiver>
  140. struct has_direct_serialization : mpl::false_ { };
  141. /**
  142. * @brief A metafunction that determines the output archiver for the
  143. * given input archiver.
  144. *
  145. * Users do not typically need to specialize this trait, as it will be
  146. * specialized as part of the macro
  147. * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
  148. */
  149. template<typename IArchiver> struct output_archiver { };
  150. /**
  151. * @brief A metafunction that determines the input archiver for the
  152. * given output archiver.
  153. *
  154. * Users do not typically need to specialize this trait, as it will be
  155. * specialized as part of the macro
  156. * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE.
  157. *
  158. */
  159. template<typename OArchiver> struct input_archiver { };
  160. namespace detail {
  161. /**
  162. * INTERNAL ONLY
  163. *
  164. * This class contains the direct-serialization code for the given
  165. * IArchiver/OArchiver pair. It is intended to be used as a
  166. * singleton class, and will be accessed when (de-)serializing a
  167. * Boost.Python object with an archiver that supports direct
  168. * serializations. Do not create instances of this class directly:
  169. * instead, use get_direct_serialization_table.
  170. */
  171. template<typename IArchiver, typename OArchiver>
  172. class BOOST_MPI_PYTHON_DECL direct_serialization_table
  173. {
  174. public:
  175. typedef boost::function3<void, OArchiver&, const object&, const unsigned int>
  176. saver_t;
  177. typedef boost::function3<void, IArchiver&, object&, const unsigned int>
  178. loader_t;
  179. typedef std::map<PyTypeObject*, std::pair<int, saver_t> > savers_t;
  180. typedef std::map<int, loader_t> loaders_t;
  181. /**
  182. * Retrieve the saver (serializer) associated with the Python
  183. * object @p obj.
  184. *
  185. * @param obj The object we want to save. Only its (Python) type
  186. * is important.
  187. *
  188. * @param descriptor The value of the descriptor associated to
  189. * the returned saver. Will be set to zero if no saver was found
  190. * for @p obj.
  191. *
  192. * @returns a function object that can be used to serialize this
  193. * object (and other objects of the same type), if possible. If
  194. * no saver can be found, returns an empty function object..
  195. */
  196. saver_t saver(const object& obj, int& descriptor)
  197. {
  198. typename savers_t::iterator pos = savers.find(obj.ptr()->ob_type);
  199. if (pos != savers.end()) {
  200. descriptor = pos->second.first;
  201. return pos->second.second;
  202. }
  203. else {
  204. descriptor = 0;
  205. return saver_t();
  206. }
  207. }
  208. /**
  209. * Retrieve the loader (deserializer) associated with the given
  210. * descriptor.
  211. *
  212. * @param descriptor The descriptor number provided by saver()
  213. * when determining the saver for this type.
  214. *
  215. * @returns a function object that can be used to deserialize an
  216. * object whose type is the same as that corresponding to the
  217. * descriptor. If the descriptor is unknown, the return value
  218. * will be an empty function object.
  219. */
  220. loader_t loader(int descriptor)
  221. {
  222. typename loaders_t::iterator pos = loaders.find(descriptor);
  223. if (pos != loaders.end())
  224. return pos->second;
  225. else
  226. return loader_t();
  227. }
  228. /**
  229. * Register the type T for direct serialization.
  230. *
  231. * @param value A sample value of the type @c T. This may be used
  232. * to compute the Python type associated with the C++ type @c T.
  233. *
  234. * @param type The Python type associated with the C++ type @c
  235. * T. If not provided, it will be computed from the same value @p
  236. * value.
  237. */
  238. template<typename T>
  239. void register_type(const T& value = T(), PyTypeObject* type = 0)
  240. {
  241. // If the user did not provide us with a Python type, figure it
  242. // out for ourselves.
  243. if (!type) {
  244. object obj(value);
  245. type = obj.ptr()->ob_type;
  246. }
  247. register_type(default_saver<T>(), default_loader<T>(type), value, type);
  248. }
  249. /**
  250. * Register the type T for direct serialization.
  251. *
  252. * @param saver A function object that will serialize a
  253. * Boost.Python object (that represents a C++ object of type @c
  254. * T) to an @c OArchive.
  255. *
  256. * @param loader A function object that will deserialize from an
  257. * @c IArchive into a Boost.Python object that represents a C++
  258. * object of type @c T.
  259. *
  260. * @param value A sample value of the type @c T. This may be used
  261. * to compute the Python type associated with the C++ type @c T.
  262. *
  263. * @param type The Python type associated with the C++ type @c
  264. * T. If not provided, it will be computed from the same value @p
  265. * value.
  266. */
  267. template<typename T>
  268. void register_type(const saver_t& saver, const loader_t& loader,
  269. const T& value = T(), PyTypeObject* type = 0)
  270. {
  271. // If the user did not provide us with a Python type, figure it
  272. // out for ourselves.
  273. if (!type) {
  274. object obj(value);
  275. type = obj.ptr()->ob_type;
  276. }
  277. int descriptor = savers.size() + 1;
  278. if (savers.find(type) != savers.end())
  279. return;
  280. savers[type] = std::make_pair(descriptor, saver);
  281. loaders[descriptor] = loader;
  282. }
  283. protected:
  284. template<typename T>
  285. struct default_saver {
  286. void operator()(OArchiver& ar, const object& obj, const unsigned int) {
  287. T value = extract<T>(obj)();
  288. ar << value;
  289. }
  290. };
  291. template<typename T>
  292. struct default_loader {
  293. default_loader(PyTypeObject* type) : type(type) { }
  294. void operator()(IArchiver& ar, object& obj, const unsigned int) {
  295. // If we can, extract the object in place.
  296. if (!is_fundamental<T>::value && obj && obj.ptr()->ob_type == type) {
  297. ar >> extract<T&>(obj)();
  298. } else {
  299. T value;
  300. ar >> value;
  301. obj = object(value);
  302. }
  303. }
  304. private:
  305. PyTypeObject* type;
  306. };
  307. savers_t savers;
  308. loaders_t loaders;
  309. };
  310. /**
  311. * @brief Retrieve the direct-serialization table for an
  312. * IArchiver/OArchiver pair.
  313. *
  314. * This function is responsible for returning a reference to the
  315. * singleton direct-serialization table. Its primary template is
  316. * left undefined, to force the use of an explicit specialization
  317. * with a definition in a single translation unit. Use the macro
  318. * BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE_IMPL to define this
  319. * explicit specialization.
  320. */
  321. template<typename IArchiver, typename OArchiver>
  322. direct_serialization_table<IArchiver, OArchiver>&
  323. get_direct_serialization_table();
  324. } // end namespace detail
  325. /**
  326. * @brief Register the type T for direct serialization.
  327. *
  328. * The @c register_serialized function registers a C++ type for direct
  329. * serialization with the given @c IArchiver/@c OArchiver pair. Direct
  330. * serialization elides the use of the Python @c pickle package when
  331. * serializing Python objects that represent C++ values. Direct
  332. * serialization can be beneficial both to improve serialization
  333. * performance (Python pickling can be very inefficient) and to permit
  334. * serialization for Python-wrapped C++ objects that do not support
  335. * pickling.
  336. *
  337. * @param value A sample value of the type @c T. This may be used
  338. * to compute the Python type associated with the C++ type @c T.
  339. *
  340. * @param type The Python type associated with the C++ type @c
  341. * T. If not provided, it will be computed from the same value @p
  342. * value.
  343. */
  344. template<typename IArchiver, typename OArchiver, typename T>
  345. void
  346. register_serialized(const T& value = T(), PyTypeObject* type = 0)
  347. {
  348. detail::direct_serialization_table<IArchiver, OArchiver>& table =
  349. detail::get_direct_serialization_table<IArchiver, OArchiver>();
  350. table.register_type(value, type);
  351. }
  352. namespace detail {
  353. /// Save a Python object by pickling it.
  354. template<typename Archiver>
  355. void
  356. save_impl(Archiver& ar, const boost::python::object& obj,
  357. const unsigned int /*version*/,
  358. mpl::false_ /*has_direct_serialization*/)
  359. {
  360. boost::python::object bytes = boost::python::pickle::dumps(obj);
  361. int sz = PyBytes_Size(bytes.ptr());
  362. char *data = PyBytes_AsString(bytes.ptr());
  363. ar << sz << boost::serialization::make_array(data, sz);
  364. }
  365. /// Try to save a Python object by directly serializing it; fall back
  366. /// on pickling if required.
  367. template<typename Archiver>
  368. void
  369. save_impl(Archiver& ar, const boost::python::object& obj,
  370. const unsigned int version,
  371. mpl::true_ /*has_direct_serialization*/)
  372. {
  373. typedef Archiver OArchiver;
  374. typedef typename input_archiver<OArchiver>::type IArchiver;
  375. typedef typename direct_serialization_table<IArchiver, OArchiver>::saver_t
  376. saver_t;
  377. direct_serialization_table<IArchiver, OArchiver>& table =
  378. get_direct_serialization_table<IArchiver, OArchiver>();
  379. int descriptor = 0;
  380. if (saver_t saver = table.saver(obj, descriptor)) {
  381. ar << descriptor;
  382. saver(ar, obj, version);
  383. } else {
  384. // Pickle it
  385. ar << descriptor;
  386. detail::save_impl(ar, obj, version, mpl::false_());
  387. }
  388. }
  389. /// Load a Python object by unpickling it
  390. template<typename Archiver>
  391. void
  392. load_impl(Archiver& ar, boost::python::object& obj,
  393. const unsigned int /*version*/,
  394. mpl::false_ /*has_direct_serialization*/)
  395. {
  396. int len;
  397. ar >> len;
  398. boost::scoped_array<char> data(new char[len]);
  399. ar >> boost::serialization::make_array(data.get(), len);
  400. boost::python::object bytes(boost::python::handle<>(PyBytes_FromStringAndSize(data.get(), len)));
  401. obj = boost::python::pickle::loads(bytes);
  402. }
  403. /// Try to load a Python object by directly deserializing it; fall back
  404. /// on unpickling if required.
  405. template<typename Archiver>
  406. void
  407. load_impl(Archiver& ar, boost::python::object& obj,
  408. const unsigned int version,
  409. mpl::true_ /*has_direct_serialization*/)
  410. {
  411. typedef Archiver IArchiver;
  412. typedef typename output_archiver<IArchiver>::type OArchiver;
  413. typedef typename direct_serialization_table<IArchiver, OArchiver>::loader_t
  414. loader_t;
  415. direct_serialization_table<IArchiver, OArchiver>& table =
  416. get_direct_serialization_table<IArchiver, OArchiver>();
  417. int descriptor;
  418. ar >> descriptor;
  419. if (descriptor) {
  420. loader_t loader = table.loader(descriptor);
  421. BOOST_ASSERT(loader);
  422. loader(ar, obj, version);
  423. } else {
  424. // Unpickle it
  425. detail::load_impl(ar, obj, version, mpl::false_());
  426. }
  427. }
  428. } // end namespace detail
  429. template<typename Archiver>
  430. void
  431. save(Archiver& ar, const boost::python::object& obj,
  432. const unsigned int version)
  433. {
  434. typedef Archiver OArchiver;
  435. typedef typename input_archiver<OArchiver>::type IArchiver;
  436. detail::save_impl(ar, obj, version,
  437. has_direct_serialization<IArchiver, OArchiver>());
  438. }
  439. template<typename Archiver>
  440. void
  441. load(Archiver& ar, boost::python::object& obj,
  442. const unsigned int version)
  443. {
  444. typedef Archiver IArchiver;
  445. typedef typename output_archiver<IArchiver>::type OArchiver;
  446. detail::load_impl(ar, obj, version,
  447. has_direct_serialization<IArchiver, OArchiver>());
  448. }
  449. template<typename Archive>
  450. inline void
  451. serialize(Archive& ar, boost::python::object& obj, const unsigned int version)
  452. {
  453. boost::serialization::split_free(ar, obj, version);
  454. }
  455. } } // end namespace boost::python
  456. /************************************************************************
  457. * Boost.MPI-Specific Section *
  458. ************************************************************************/
  459. namespace boost { namespace mpi {
  460. class packed_iarchive;
  461. class packed_oarchive;
  462. } } // end namespace boost::mpi
  463. BOOST_PYTHON_DIRECT_SERIALIZATION_ARCHIVE(
  464. ::boost::mpi::packed_iarchive,
  465. ::boost::mpi::packed_oarchive)
  466. namespace boost { namespace mpi { namespace python {
  467. template<typename T>
  468. void
  469. register_serialized(const T& value, PyTypeObject* type)
  470. {
  471. using boost::python::register_serialized;
  472. register_serialized<packed_iarchive, packed_oarchive>(value, type);
  473. }
  474. } } } // end namespace boost::mpi::python
  475. #endif // BOOST_MPI_PYTHON_SERIALIZE_HPP