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