1 /* _________________________________________________________________________
2 *
3 * UTILIB: A utility library for developing portable C++ codes.
4 * Copyright (c) 2008 Sandia Corporation.
5 * This software is distributed under the BSD License.
6 * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7 * the U.S. Government retains certain rights in this software.
8 * For more information, see the README file in the top UTILIB directory.
9 * _________________________________________________________________________
10 */
11
12 /**
13 * \file Serialize.h
14 *
15 * Defines:
16 * - utilib::SerialObject struct
17 * - utilib::SerialPOD class
18 * - utilib::Serialization_Manager class
19 * - utilib::Serializer() singleton
20 *
21 */
22
23 #ifndef utilib_Serialize_h
24 #define utilib_Serialize_h
25
26 #include <utilib/Any.h>
27
28 namespace utilib {
29
30 class Serialization_Manager;
31
32 namespace error {
33 namespace Serialization {
34 const int EmptyData = -300;
35 const int ExtraData = -301;
36 const int UnknownType = -302;
37 const int DuplicateRegistration = -303;
38 const int BadPODData = -304;
39 const int BadPODTextConversion = -305;
40 const int MissingStringQuote = -306;
41 const int UnconvertedPODText = -307;
42 } // namespace utilib::error::Serialization
43 } // namespace utilib::error
44
45
46 /// Return the system-wide Serialization_Manager singleton.
47 Serialization_Manager& Serializer();
48
49 /// Generic serializer exception from which all other exceptions will inherit
50 class serialization_error : public std::runtime_error
51 {
52 public:
53 /// Constructor
serialization_error(const std::string & msg)54 serialization_error(const std::string& msg)
55 : runtime_error(msg)
56 {}
57 };
58
59
60 class serializer_unknown_type : public serialization_error
61 {
62 public:
63 /// Constructor
serializer_unknown_type(const std::string & msg)64 serializer_unknown_type(const std::string& msg)
65 : serialization_error(msg)
66 {}
67 };
68
69 class serializer_bad_pod_size : public serialization_error
70 {
71 public:
72 /// Constructor
serializer_bad_pod_size(const std::string & msg)73 serializer_bad_pod_size(const std::string& msg)
74 : serialization_error(msg)
75 {}
76 };
77
78
79 /// A data "pseudo-object" for containing serialized Plain-ol-Data types
80 class SerialPOD
81 {
82 public:
SerialPOD()83 SerialPOD()
84 : buffer(),
85 text_mode(false)
86 {}
SerialPOD(size_t init_size)87 SerialPOD(size_t init_size)
88 : buffer(init_size),
89 text_mode(false)
90 {}
91
92 /// Set the contents of this SerialPOD (binary mode)
93 void set(const void* buffer_src, const size_t length);
94
95 /// Set the contents of this SerialPOD (text mode)
96 void set(const std::string& buffer_src);
97
98 /// Set the contents of this SerialPOD (text mode)
is_text_mode()99 bool is_text_mode() const
100 { return text_mode; }
101
102 /// Print out the stream of character values for this SerialPOD
103 void print(std::ostream& os, std::string indent = "") const;
104
size()105 size_t size() const
106 { return buffer.size(); }
data()107 const char* data() const
108 { return &buffer[0]; }
109
110 private:
111 std::vector<char> buffer;
112 bool text_mode;
113 };
114
115
116 /// A structural "pseudo-object" for representing a class / structure
117 class SerialObject
118 {
119 public:
SerialObject()120 SerialObject()
121 : type(0),
122 data()
123 {}
SerialObject(size_t type_id)124 SerialObject(size_t type_id)
125 : type(type_id),
126 data()
127 {}
128
129 void print(std::ostream& os, std::string indent = "") const;
130
131 /// A list of SerialObjects (each element is a serialized object member)
132 typedef std::list<SerialObject> elementList_t;
133
134 size_t type;
135 Any data;
136 };
137
138
139
140 /// The prototype for serilization/deserialization transformation functions
141 typedef int(*transform_fcn)(SerialObject::elementList_t&, Any&, bool);
142 /// The prototype for POD serialization/deserialization functions
143 typedef int(*pod_transform_fcn)(SerialPOD&, Any&, bool);
144 /// The prototype for serialization/deserialization POD-to-text functions
145 typedef int(*pod_text_transform_fcn)(std::string&, Any&, bool);
146 /// The prototype for an instantiation function (populate the provided Any)
147 typedef void(*initialization_fcn)(Any&);
148
149
150 /// Class for storing and managing all serialization functions
151 /** This class stores all serialization and type initialization function
152 * pointers and coordinates the process of serializing and
153 * deserializing data types. It is published to the outside world
154 * through the Serializer() singleton.
155 *
156 * The power of the utilib::Serialization_Manager and the
157 * characteristic that distinguishes it from other serialization
158 * approaches is its compatibility with the utilib::Any variant data
159 * type. Most serialization approaches directly leverage templates to
160 * have the compiler insert the correct serialization / deserialization
161 * functions at compile time. While this works well for concrete types
162 * (and even templated concrete types), it is incompatible with the
163 * utilib::Any variant data type, as the compiler cannot know what data
164 * is contained within the Any at compile time. To facilitate the
165 * serialization of Anys the Serialization_Manager uses a registration
166 * system where serializable classes register their serialization
167 * functions with the manager, allowing the manager to dynamically
168 * locate and bind to the correct serialization function at runtime.
169 *
170 * \par Interacting with the Serialization_Manager
171 *
172 * Apart from registering new user-defined serializers (with
173 * Serialization_Manager::register_serializer()), the general public
174 * should never directly call the Serialization_Manager member
175 * functions. Instead, the general public should make use of the
176 * utilib namespace-level functions:
177 * - ::Serialize
178 * - ::Deserialize
179 * - ::serial_transform
180 *
181 * In general, "users" of the serialization system will call
182 * Serialize() and Deserialize(). As expected, Serialize() takes any
183 * object and returns the corresponding SerialObject, and Deserialize()
184 * takes a SerialObject and returns the deserialized object within an
185 * Any. In essence, Serialize() and Deserialize() are convenience
186 * wrappers around serial_transform()
187 *
188 * serial_transform() is a utility function to assist developers in
189 * extending serialization capabilities by defining and registering new
190 * serializer functions for user-defined types. It is a wrapper around
191 * Serialization_Manager::transform_impl() that provides a mechanism
192 * for guaranteeing that serializers for template class instantiations
193 * are registered \em before they are encountered during a
194 * serialization operation. This is covered in more detail in the
195 * "Registering your serialization functions" section below.
196 *
197 * \par Serialization Functions
198 *
199 * There are three fundamental serialization functions supported by the
200 * Serialization_Manager. The first (::pod_transform_fcn) is for
201 * serializing and deserializing "Plain 'ol Data" (POD) types. It will
202 * take the variable and convert it to/from a SerialPOD object by
203 * storing the variable's binary data as a char vector. The
204 * Serialization_Manager defines and registers serializers for all
205 * non-pointer standard C++ POD data types.
206 *
207 * The second serialization function (::transform_fcn) is for
208 * serializing user-defined objects. This function converts the
209 * contents the object in question into a SerialObject list, usually by
210 * successively calling serial_transform(). For example, consider the
211 * following user-defined class fragment and its (valid) serializer:
212 *
213 * The third serialization function (::pod_text_transform_fcn) is not
214 * necessarily part of the core Serialization_Manager functionality,
215 * but is maintained here to assist some writers and parsers for
216 * serialized objects. This serialization function converts POD data
217 * types to and from a char-based (not wchar_t-based) std::string. The
218 * Serialization Manager will automatically fall back to the normal
219 * (binary) transformation if the text serialization function is NULL
220 * (or not provided). All standard C++ POD types provide valid text
221 * serialization functions, with the exception of wchar_t.
222 *
223 * \code
224 * class A
225 * {
226 * // ...
227 * private:
228 * static int serializer( utilib::SerialObject::elementList_t & serial,
229 * utilib::Any & data, bool serialize )
230 * {
231 * A& me = const_cast<A&>(data.expose<A>());
232 * serial_transform(serial, me.a, serialize);
233 * serial_transform(serial, me.b, serialize);
234 * serial_transform(serial, me.c, serialize);
235 * return 0;
236 * }
237 *
238 * int a;
239 * float b;
240 * std::list<double> c;
241 *
242 * static const volatile int serializer_registered;
243 * };
244 * \endcode That's it!
245 *
246 * Both ::pod_transform_fcn and ::transform_fcn serialization functions
247 * are registered by calling one of four overloaded
248 * Serializer().register_serializer() template functions.
249 * register_serializer() takes a "user-defined" name, the transform
250 * function, and an optional initialization function that returns a new
251 * valid instance of the class within an Any (::initialization_fcn).
252 * If not provided, ::initialization_fcn defaults to using the Default
253 * Constructor.
254 *
255 * \par Registering your serialization functions
256 *
257 * In general, registering a serialization function entails notifying
258 * the Serialization_Manager of a relationship between a \c typeid, a
259 * user-defined name, a serialization function, and optionally, a
260 * constructor. For more information on creating user-defined type
261 * names, see the section "Providing a user-defined name" below.
262 *
263 * Proper registration of a class serialization function with the
264 * global Serializer() singleton is the critical step in getting the
265 * serialization process working correctly. One of the most important
266 * aspects is to perform all registrations as \em early in the program
267 * executaion as possible. Unlike template-based serialization
268 * strategies, it is possible to DEserialize an object of a particular
269 * class \em before that class is ever actually used in the program.
270 * As a result,the preferable way to register a serializer is through
271 * static variable initialization \em before entry into \c main().
272 * While there are many nuances to the process (most revolving around
273 * the C++ compiler and linker), most situations should fall into one
274 * of 4 basic categories:
275 *
276 * \par 1) A concrete class you control
277 *
278 * This is probably the most straightforward situation. You have a
279 * concrete class that you have control over. In this case, the
280 * simplest and most robust approach is to intrusively hook your class
281 * to the utilib::Serializer(). As in the <tt>class A</tt> example
282 * above, make the serialization function a private static member of
283 * the class, and add a <tt>private static const volatile int</tt>. In
284 * the corresponding code (\c .cpp) file, register the serialization
285 * function by initializing the static const:
286 *
287 * \code
288 * const volatile int A::serializer_registered
289 * = utilib::Serializer().register_serializer<A>("A", A::serializer);
290 * \endcode
291
292 * By tying the registration to the initialization of a static
293 * variable, simply using the class \em somewhere in the program will
294 * cause the serializer to be registered \em before entry into \c
295 * main(). Note, as no arguments depend on the template parameter (the
296 * type being serialized), you must explicitly specify it when invoking
297 * register_serializer(). Also note the use of the "volatile" keyword.
298 * This helps prevent linkers from "optimizing away" what is otherwise
299 * effectively a no-op.
300 *
301 * \par 2) A template class you control
302 *
303 * Templates are slightly more complicated than concrete classes, as
304 * most compilers only emit template code that is actuallr \em
305 * references somewhere in the program executable. The solution is to
306 * explicitly reference the static registration variable in \b all
307 * constructors (including any copy constructors):
308 *
309 * \code
310 * template<typename T1, typename T2>
311 * class B
312 * {
313 * public:
314 * B() { static_cast<void>(serializer_registered); }
315 *
316 * B(const B& rhs)
317 * {
318 * static_cast<void>(serializer_registered);
319 * // ...
320 * }
321 *
322 * private:
323 * static int serializer( utilib::SerialObject::elementList_t & serial,
324 * utilib::Any & data, bool serialize )
325 * {
326 * B& me = const_cast<B&>(data.expose<B>());
327 * serial_transform(serial, me.a, serialize);
328 * serial_transform(serial, me.b, serialize);
329 * serial_transform(serial, me.c, serialize);
330 * return 0;
331 * }
332 *
333 * T1 a;
334 * T2 b;
335 * std::list<T1> c;
336 *
337 * static const volatile int serializer_registered;
338 * };
339 *
340 * template<typename T1, typename T2>
341 * const volatile int A<T1,T2>::serializer_registered
342 * = utilib::Serializer().register_serializer<A<T1,T2> >
343 * ( std::string("B;") + utilib::mangledName(typeid(T1)) + ";"
344 * + utilib::mangledName(typeid(T2)),
345 B<T1,T2>::serializer );
346 * \endcode
347 *
348 * In this case, the static const is initialized through a templated
349 * initializer, and the \c static_cast<void>(serializer_registered)
350 * lines create explicit references to cause the compiler to emit,
351 * link, and register the serialization function for every
352 * instantiation of the template class. Note again that the use of the
353 * \c volatile keyword is critical in this scenario.
354 *
355 * \par 3) A concrete class that you do \em not control
356 *
357 * This case is a relatively trivial extension of case (1). In this
358 * case, the class is owned or controled by someone else, so we cannot
359 * directly edit the class API. Given a class \c C, construct a
360 * standalone serialization function and register it through a local
361 * static variable:
362 *
363 * \code
364 * class C; // defined elsewhere
365 *
366 * namespace {
367 * int serialize_C( utilib::SerialObject::elementList_t & serial,
368 * utilib::Any & data, bool serialize )
369 * {
370 * C& me = const_cast<C&>(data.expose<C>());
371 * // ... (de)serialize through C's public API ...
372 * return 0;
373 * }
374 *
375 * const int c_registered
376 * = utilib::Serializer().register_serializer<C>("C", serialize_C);
377 * } // namespace (local)
378 * \endcode
379 *
380 * Note that this entire code fragment appears in a \em source (and not
381 * header) file. The one "gotcha" is that if this registration is
382 * being done as part of a static library (i.e. \c *.a), it will only
383 * be picked up by the linker if something directly referenced by \c
384 * main() in turn references something in the source in which you
385 * placed the registration code.
386 *
387 * \par 4) A template class that you do \em not control
388 *
389 * Serializing template classes controlled by others are perhaps the
390 * most complicated example. While individual serializations for
391 * specific template instantiations may be handled as in case (3), the
392 * general case that provides serialization for \em any instantiation
393 * of the template is more involved. A prime example of this is the
394 * STL, for which we provide general serialization support at the
395 * bottom of this file. In this case, the general pattern is to make
396 * use of the serial_transform template family. In a header (included
397 * before any and all places where the template class is serialized),
398 * create the following:
399 *
400 * \code
401 * // Given (defined elsewhere):
402 * template<typename T2, typename T2> class D;
403 *
404 * template<typename T1, typename T2>
405 * int serialize_D( utilib::SerialObject::elementList_t & serial,
406 * utilib::Any & data, bool serialize )
407 * {
408 * D& me = const_cast<D&>(data.expose<D>());
409 * // ... (de)serialize through D's public API ...
410 * return 0;
411 * }
412 *
413 * template<typename T1, typename T2>
414 * struct D_registrar {
415 * static const volatile int registered;
416 * };
417 * template<typename T1, typename T2>
418 * const volatile int D_registrar<T1,T2>::registered
419 * = Serializer().register_serializer< D<T1,T2> >
420 * ( std::string("D;") + mangledName(typeid(T1)) + ";"
421 * + mangledName(typeid(T2)), serialize_D<T1,T2> );
422 *
423 * namespace utilib {
424 * template<typename T1, typename T2>
425 * int serial_transform( SerialObject::elementList_t& serial,
426 * D<T1,T2>& data, bool serialize )
427 * {
428 * (void)&D_registrar<T1,T2>::registered;
429 * AnyFixedRef tmp = data;
430 * return Serializer().transform_impl( typeid(data), serial,
431 * tmp, serialize );
432 * }
433 * } // namespace (local)
434 * \endcode
435 *
436 * Note that unlike cases (1) and (2), where simply \em using the class
437 * somewhere in your program will cause the serialization function to
438 * be automatically registered with the Serializer(), in this case, the
439 * serializer will only be registered is the class is directly or
440 * indirectly explicitly serialized somewhere in the program. Indirect
441 * references include other classes that serialize the class in
442 * question as part of \em their serializer. For example, \c
443 * std::list<double> is indirectly referenced by the serializer for \c
444 * A above, and serializing \c std::map<int,std::list<int>> will
445 * automatically register the \c std::list<int> serializer.
446 *
447 * If it is necessary to \em guarantee that a particular template
448 * instantiation is registered, simply reference it in a source file,
449 * as was done in case (3):
450 *
451 * \code
452 * namespace {
453 * const int force_registration_1 = D_registrar<T1,T2>::registered;
454 * } // namespace (local)
455 * \endcode
456 *
457 * \par Using the Serializer() before \c main()
458 *
459 * As with all computations that occur before entry into \c main(),
460 * particular care must be taken to make sure that static data is not
461 * referenced before it is initialized. As a general rule, <b><i>the
462 * Serializer() should not be used before entry into main()</i></b>.
463 * If it is absolutely necessary, there are two approaches that may
464 * work, although both rely on creating (and including)
465 * serial_transform() definitions for <b>\em all</b> classes that will be
466 * serialized. The first approach is to construct the
467 * serial_transform() template similar to case (4):
468 *
469 * \code
470 * template<typename T1, typename T2>
471 * struct D_registrar {
472 * static const volatile int registered;
473 * const int _register() {
474 * return Serializer().register_serializer<D<T1,T2> >
475 * ( std::string("D;") + mangledName(typeid(T1)) + ";"
476 * + mangledName(typeid(T2)), serialize_D<T1,T2> );
477 * }
478 * };
479 * template<typename T1, typename T2>
480 * const volatile int
481 * D_registrar<T1,T2>::registered = D_registrar<T1,T2>::_register()
482 *
483 * namespace utilib {
484 * template<typename T1, typename T2>
485 * int serial_transform( SerialObject::elementList_t& serial,
486 * std::list<T1,T2>& data, bool serialize )
487 * {
488 * D_registrar<T1,T2>::registered || D_registrar<T1,T2>::_register();
489 * AnyFixedRef tmp = data;
490 * return Serializer().transform_impl( typeid(data), serial,
491 * tmp, serialize );
492 * }
493 * } // namespace (local)
494 * \endcode
495 *
496 * While this approach has been observed to be successful, it relies on
497 * the uninitialized \c registered variable to default to \c false.
498 * While in some instances this may be accuralte, in general it is not
499 * safe to rely on uninitialized values. A more reliable alternative
500 * is to provide another level of indirection:
501 *
502 * \code
503 * template<typename T1, typename T2>
504 * struct D_registrar {
505 * static const volatile int auto_register;
506 * const int registered() {
507 * static const int ans = Serializer().register_serializer<D<T1,T2> >
508 * ( std::string("D;") + mangledName(typeid(T1)) + ";"
509 * + mangledName(typeid(T2)), serialize_D<T1,T2> );
510 * return ans;
511 * }
512 * };
513 * template<typename T1, typename T2>
514 * const volatile int
515 * D_registrar<T1,T2>::auto_register = D_registrar<T1,T2>::registered()
516 *
517 * namespace utilib {
518 * template<typename T1, typename T2>
519 * int serial_transform( SerialObject::elementList_t& serial,
520 * std::list<T1,T2>& data, bool serialize )
521 * {
522 * (void)&D_registrar<T1,T2>::auto_register;
523 * D_registrar<T1,T2>::registered();
524 * AnyFixedRef tmp = data;
525 * return Serializer().transform_impl( typeid(data), serial,
526 * tmp, serialize );
527 * }
528 * } // namespace (local)
529 * \endcode
530 *
531 * While this solution is more robust, it imposes the additional
532 * overhead of a function call for \em every serialization. The
533 * preferred mode is to simply not perform any serialization actions
534 * until after entry into \c main().
535 *
536 * \par Providing a user-defined name
537 *
538 * The user-defined class name provides the Serializer with a unique
539 * compiler-independent "human-readable" form of the class name.
540 * Internally the Serializer relies strictly on the type_info structure
541 * and the mangled type name returned by utilib::mangledName() [\b NB:
542 * ALWAYS use \c utilib::mangledName() and \em NOT \c
543 * std::type_info::name() because Microsoft's Visual C++ implementation
544 * of \c std::type_info::name() does NOT return the mangled name].
545 * However, C++ mangling rules are compiler-dependent and keying off
546 * them would restrict serialized data from a program to only be
547 * readable by other programs (or instances of the same program)
548 * compiled by the same compiler. To alleviate this, we build a
549 * secondary user-specified naming system to use for storing data in a
550 * more generic format.
551 *
552 * A general rule for generating a user-defined name is to use the
553 * human-readable class name, \b including any namespaces.
554 *
555 * While the process of giving unique names for standard classes is
556 * relatively straightforward, doing so for each instantiation of a
557 * template class is tedious at best. To relieve part of this burden,
558 * the Serializer contains explicit support for recursively resolving
559 * the registered names of template parameters for you. To register a
560 * serializer for a template class, the "user-defined" name is
561 * typically the name of your class, followed by the \em mangled type
562 * names for each of the template arguments (all fields separated by
563 * semicolons). For example, given a class B templated on two types,
564 * the registration for the instance <tt>B<int,std::vector<double>
565 * ></tt> would be:
566 *
567 * \code
568 * template<typename T1, typename T2>
569 * class B
570 * {
571 * // ...
572 * private:
573 * static int serializer( utilib::SerialObject::elementList_t & serial,
574 * utilib::Any & data, bool serialize )
575 * static const volatile int serializer_registered;
576 * };
577 *
578 * template<>
579 * const volatile int B<int, std::vector<double> >::serializer_registered
580 * = Serializer().register_serializer<B<int, std::vector<double> > >
581 * ( std::string("B;") + utilib::mangledName(typeid(int)) + ";"
582 * + utilib::mangledName(typeid(std::vector<double>)),
583 * B<int, std::vector<double> >::serializer );
584 * \endcode
585 *
586 * While awkward for single instantiations, this approach allows the
587 * registration itself to be templitized, thus supporting "automatic"
588 * registration (see the registration discuaaion above, ::Serialize(),
589 * and the STL_SerialRegistrars and STL_Serializers namespaces):
590 *
591 * \code
592 * template<typename T1, typename T2>
593 * const volatile int B<T1,T2>::serializer_registered
594 * = Serializer().register_serializer<B<T1,T2> >
595 * ( std::string("B;") + utilib::mangledName(typeid(T1)) + ";"
596 * + utilib::mangledName(typeid(T2)),
597 * B<T1,T2>::serializer );
598 * \endcode
599 */
600 class Serialization_Manager
601 {
602 public:
603 /// A flag indicating the endianness of this platform
604 static const unsigned char Endian;
605
606 /// Constructor - registers standard C++ POD types
607 Serialization_Manager();
608 /// Destructor
609 ~Serialization_Manager();
610
611 /// Retrieve the typeinfo for the specified type key
612 const std::type_info* get_typeinfo(size_t key);
613
614 /// Retrieve the user-defined name for the specified type key
615 std::string get_username(size_t key);
616
617 /// Return true if the key is a POD
618 bool is_pod(size_t key);
619
620 /// Return true if the key is a POD
621 int get_pod_length(size_t key);
622
623 /// Retrieve the type key for the specified user-defined name
624 size_t get_keyid(std::string user_name);
625
626 /// Explicit alias for get_keyid(string) to avoid ambiguity under Solaris
get_keyid(const char * user_name)627 size_t get_keyid(const char* user_name)
628 { return get_keyid(std::string(user_name)); }
629
630 /// Retrieve the type key for the specified typeinfo
631 size_t get_keyid(const std::type_info &type);
632
633 /// Set whether POD transformations are into text format instead of binary
set_pod_text_mode(bool pod_text_mode)634 void set_pod_text_mode(bool pod_text_mode)
635 { serialize_pod_as_text = pod_text_mode; }
636
637 /// Perform the serialize/deserialize transformation
638 int transform_impl(const std::type_info& type,
639 SerialObject::elementList_t& serial,
640 AnyFixedRef& data,
641 bool serialize);
642
643 /// Register a new serialization function (use the default initializer)
644 template<typename T>
register_serializer(std::string name,transform_fcn fcn)645 int register_serializer(std::string name, transform_fcn fcn)
646 {
647 return register_serializer<T>( name, fcn, &DefaultInitializer<T> );
648 }
649
650 /// Register a new serialization function and the specified initializer
651 template<typename T>
register_serializer(std::string name,transform_fcn fcn,initialization_fcn init)652 int register_serializer( std::string name,
653 transform_fcn fcn, initialization_fcn init )
654 {
655 return register_serializer( typeid(T), name, -1, fcn, NULL, NULL, init );
656 }
657
658 /// Register a new POD serialization function (use the default initializer)
659 template<typename T>
660 int register_serializer(std::string name, pod_transform_fcn fcn,
661 pod_text_transform_fcn tfcn = NULL,
662 int size = static_cast<int>(sizeof(T)) )
663 {
664 return register_serializer<T>( name, fcn, tfcn, size,
665 &DefaultInitializer<T> );
666 }
667
668 /// Register a new POD serialization function and the specified initializer
669 template<typename T>
register_serializer(std::string name,pod_transform_fcn fcn,pod_text_transform_fcn tfcn,int size,initialization_fcn init)670 int register_serializer( std::string name, pod_transform_fcn fcn,
671 pod_text_transform_fcn tfcn, int size,
672 initialization_fcn init )
673 {
674 return register_serializer(typeid(T), name, size, NULL, fcn, tfcn, init);
675 }
676
677
678 /// Debugging function: list all known transformation functions
679 void list_serializers(std::ostream& os);
680
681 public: // sub-classes
682
683 /// The standard default initializer: use the class default constructor
684 template<typename T>
DefaultInitializer(Any & data)685 static void DefaultInitializer(Any& data)
686 {
687 data.set<T>();
688 }
689
690 private: // sub-classes
691 typedef std::pair<std::string, size_t> typename_pair_t;
692 typedef std::map<std::string, size_t> typename_map_t;
693 typedef std::pair<const std::type_info*, size_t> type_pair_t;
694 typedef std::map<const std::type_info*, size_t> type_map_t;
695 typedef std::pair<std::string, typename_map_t::iterator> username_pair_t;
696 typedef std::map<std::string, typename_map_t::iterator> username_map_t;
697
698 /// Container for holding serializer information about a type
699 struct MappingFunctions {
700 initialization_fcn init; ///< initialization: return new object
701 transform_fcn transform; ///< serialize to/from SerialObject
702 pod_transform_fcn pod_transform; ///< serialize to/from SerialPOD
703 pod_text_transform_fcn pod_txt_xform; ///< serialize POD to/from text
704 const std::type_info* typeinfo; ///< The main type_info for this type
705 std::string raw_user_name; ///< Name specified by user
706 int pod_size; ///< Data size (-1==variable)
707 username_map_t::iterator username; ///< Ref.s to resolved user name
708 };
709
710 private: // methods
711 /// Actual method to register a new serializer with the system
712 int register_serializer( const std::type_info& type,
713 std::string name,
714 int pod_size,
715 transform_fcn t_fcn,
716 pod_transform_fcn p_fcn,
717 pod_text_transform_fcn pt_fcn,
718 initialization_fcn i_fcn);
719
720 /// (Utility method) Rebuild the user-defined type name map
721 void rebuild_user_name_map();
722
723 /// (Utility method) Recursively resolve any templated types for a user name
724 std::string resolve_user_name(std::string mangled);
725
726 private: // data
727 /// Map mangled names to the index in the functions vector
728 typename_map_t typename_map;
729 /// Map user name to mangled name (actually, iterator into typename_map)
730 username_map_t username_map;
731 /// Map type_info to the index in the functions vector
732 type_map_t type_map;
733 /// The vector of all serialization information
734 std::vector<MappingFunctions> functions;
735 /// If true, username_map is out-of-date and must be rebuilt
736 bool rebuild_usernames;
737 /// If true, transform_impl will serialize PODs using pod_text_transform_fcn
738 bool serialize_pod_as_text;
739 };
740
741
742 //-----------------------------------------------------------------------
743 // Declare all STL-based serializers (implemented below)
744 //
745 namespace STL_Serializers {
746
747 template<typename T>
748 int pair_serializer( SerialObject::elementList_t& serial,
749 Any& data, bool serialize );
750
751 template<typename T>
752 int sequence_serializer( SerialObject::elementList_t& serial,
753 Any& data, bool serialize );
754
755 template<typename T>
756 int adaptor_serializer( SerialObject::elementList_t& serial,
757 Any& data, bool serialize );
758
759 template<typename T>
760 int set_serializer( SerialObject::elementList_t& serial,
761 Any& data, bool serialize );
762
763 template<typename T>
764 int map_serializer( SerialObject::elementList_t& serial,
765 Any& data, bool serialize );
766
767 } // namespace STL_Serializers
768
769
770
771 //-----------------------------------------------------------------------
772 /** All serialization registrars for standard STL classes. This set of
773 * templated namespace-level constants assists in the automagic
774 * registration of STL template serializers. The key trick here is to
775 * get the \b instantiation of the wrapper function (see
776 * serial_transform() below) to cause the registration of the
777 * corresponding serializer \b BEFORE Serialize() / Deserialize() is
778 * ever called. If we just took care of the registration through a
779 * static variable within the serialization wrapper function, the
780 * serializer would not get registered until the first call to the
781 * serialization wrapper function. The problem with that is that the
782 * program may attempt to DEserialize that type (i.e. loading from a
783 * file) BEFORE ever serializing the type. Thus, it is critical that
784 * the registration happens early, preferably before entry into main().
785 * These templates take care of that.
786 *
787 * Note that we are using \b both a static volatile variable
788 * initialization and a registration function. The static volatile
789 * initialization causes the serializer registration to occur before
790 * entry into main(). The definition of the _register() function is
791 * used below in the serial_transform() template to guard against other
792 * classes using the serializer before entry into main(). If another
793 * class (see the BinarySerialStream unit tests) calls the serializer
794 * during static construction, the static volatile registration flag
795 * may not yet be initialized. By testing the flag, and if false,
796 * explicitly calling the _register() function, automagic registration
797 * should still correctly occur.
798 */
799 namespace STL_SerialRegistrars {
800
801 template<typename T1, typename T2>
802 struct pair_registrar {
803 static const volatile int registered;
804 };
805 template<typename T1, typename T2>
806 const volatile int pair_registrar<T1,T2>::registered = Serializer()
807 .template register_serializer<std::pair<T1,T2> >
808 ( std::string("std::pair;")
809 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2)),
810 STL_Serializers::pair_serializer<std::pair<T1,T2> > );
811
812
813
814 template<typename T1, typename T2>
815 struct list_registrar {
816 static const volatile int registered;
817 };
818 template<typename T1, typename T2>
819 const volatile int list_registrar<T1,T2>::registered = Serializer()
820 .template register_serializer<std::list<T1,T2> >
821 ( std::string("std::list;")
822 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2)),
823 STL_Serializers::sequence_serializer<std::list<T1,T2> > );
824
825 template<typename T1, typename T2>
826 struct vector_registrar {
827 static const volatile int registered;
828 };
829 template<typename T1, typename T2>
830 const volatile int vector_registrar<T1,T2>::registered = Serializer()
831 .template register_serializer<std::vector<T1,T2> >
832 ( std::string("std::vector;")
833 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2)),
834 STL_Serializers::sequence_serializer<std::vector<T1,T2> > );
835
836 template<typename T1, typename T2>
837 struct deque_registrar {
838 static const volatile int registered;
839 };
840 template<typename T1, typename T2>
841 const volatile int deque_registrar<T1,T2>::registered = Serializer()
842 .template register_serializer<std::deque<T1,T2> >
843 ( std::string("std::deque;")
844 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2)),
845 STL_Serializers::sequence_serializer<std::deque<T1,T2> > );
846
847
848
849 template<typename T1, typename T2>
850 struct queue_registrar {
851 static const volatile int registered;
852 };
853 template<typename T1, typename T2>
854 const volatile int queue_registrar<T1,T2>::registered = Serializer()
855 .template register_serializer<std::queue<T1,T2> >
856 ( std::string("std::queue;")
857 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2)),
858 STL_Serializers::adaptor_serializer<std::queue<T1,T2> > );
859
860 template<typename T1, typename T2>
861 struct stack_registrar {
862 static const volatile int registered;
863 };
864 template<typename T1, typename T2>
865 const volatile int stack_registrar<T1,T2>::registered = Serializer()
866 .template register_serializer<std::stack<T1,T2> >
867 ( std::string("std::stack;")
868 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2)),
869 STL_Serializers::adaptor_serializer<std::stack<T1,T2> > );
870
871 template<typename T1, typename T2, typename T3>
872 struct priority_queue_registrar {
873 static const volatile int registered;
874 };
875 template<typename T1, typename T2, typename T3>
876 const volatile int priority_queue_registrar<T1,T2,T3>::registered =Serializer()
877 .template register_serializer<std::priority_queue<T1,T2,T3> >
878 ( std::string("std::priority_queue;") + mangledName(typeid(T1)) + ";"
879 + mangledName(typeid(T2)) + ";" + mangledName(typeid(T3)),
880 STL_Serializers::adaptor_serializer<std::priority_queue<T1,T2,T3> > );
881
882
883
884 template<typename T1, typename T2, typename T3>
885 struct set_registrar {
886 static const volatile int registered;
887 };
888 template<typename T1, typename T2, typename T3>
889 const volatile int set_registrar<T1,T2,T3>::registered = Serializer()
890 .template register_serializer<std::set<T1,T2,T3> >
891 ( std::string("std::set;") + mangledName(typeid(T1)) + ";"
892 + mangledName(typeid(T2)) + ";" + mangledName(typeid(T3)),
893 STL_Serializers::set_serializer<std::set<T1,T2,T3> > );
894
895 template<typename T1, typename T2, typename T3>
896 struct multiset_registrar {
897 static const volatile int registered;
898 };
899 template<typename T1, typename T2, typename T3>
900 const volatile int multiset_registrar<T1,T2,T3>::registered = Serializer()
901 .template register_serializer<std::multiset<T1,T2,T3> >
902 ( std::string("std::multiset;") + mangledName(typeid(T1)) + ";"
903 + mangledName(typeid(T2)) + ";" + mangledName(typeid(T3)),
904 STL_Serializers::set_serializer<std::multiset<T1,T2,T3> > );
905
906
907
908 template<typename T1, typename T2, typename T3, typename T4>
909 struct map_registrar {
910 static const volatile int registered;
911 };
912 template<typename T1, typename T2, typename T3, typename T4>
913 const volatile int map_registrar<T1,T2,T3,T4>::registered = Serializer()
914 .template register_serializer<std::map<T1,T2,T3,T4> >
915 ( std::string("std::map;")
916 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2))
917 + ";" + mangledName(typeid(T3)) + ";" + mangledName(typeid(T4)),
918 STL_Serializers::map_serializer<std::map<T1,T2,T3,T4> > );
919
920 template<typename T1, typename T2, typename T3, typename T4>
921 struct multimap_registrar {
922 static const volatile int registered;
923 };
924 template<typename T1, typename T2, typename T3, typename T4>
925 const volatile int multimap_registrar<T1,T2,T3,T4>::registered = Serializer()
926 .template register_serializer<std::multimap<T1,T2,T3,T4> >
927 ( std::string("std::multimap;")
928 + mangledName(typeid(T1)) + ";" + mangledName(typeid(T2)) + ";"
929 + mangledName(typeid(T3)) + ";" + mangledName(typeid(T4)),
930 STL_Serializers::map_serializer<std::multimap<T1,T2,T3,T4> > );
931
932 } // namespace STL_SerialRegistrars
933
934
935
936 //-----------------------------------------------------------------------
937 //
938
939 /** \param[in,out] serial A SerialObject list
940 * \param[in,out] data A reference to the variable to serialize
941 * \param[in] serialize Indicates whether to serialize or deserialize
942 * \return 0 on success, <>0 on error.
943 *
944 * serial_transform() is a "convenience" wrapper function around the
945 * core Serialization_Manager::transform_impl() function. To provide
946 * implicit support for template classes (including the STL), we need
947 * to assure that the serialization for a particular template
948 * instantiation is registered before the first call to
949 * Serialization_Manager::transform_impl(). The templated overloads of
950 * this namespace-level wrapper function guarantee that the
951 * registration takes place.
952 *
953 * This specific function is designed to correctly deal with
954 * serializing and deserializing Any variables (with special care to
955 * correctly process empty Any data).
956 *
957 * Each call to serial_transform() with \a serialize == true will
958 * serialize the variable referenced by \a data into a SerialObject and
959 * push the resulting object onto the end of the \a serial list. Each
960 * call to serial_transform with \a serialize == false will remove the
961 * SerialObject from the front of the \a serial list and deserialize it
962 * into the Any \a data object.
963 */
964 int serial_transform(SerialObject::elementList_t& serial,
965 Any& data, bool serialize);
966
967 template<typename T>
serial_transform(SerialObject::elementList_t & serial,T & data,bool serialize)968 int serial_transform(SerialObject::elementList_t& serial,
969 T& data, bool serialize)
970 {
971 AnyFixedRef tmp = data;
972 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
973 }
974
975 template<typename T1, typename T2>
serial_transform(SerialObject::elementList_t & serial,std::pair<T1,T2> & data,bool serialize)976 int serial_transform(SerialObject::elementList_t& serial,
977 std::pair<T1,T2>& data, bool serialize)
978 {
979 (void)&STL_SerialRegistrars::pair_registrar<T1,T2>::registered;
980 AnyFixedRef tmp = data;
981 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
982 }
983
984 template<typename T1, typename T2>
serial_transform(SerialObject::elementList_t & serial,std::list<T1,T2> & data,bool serialize)985 int serial_transform(SerialObject::elementList_t& serial,
986 std::list<T1,T2>& data, bool serialize)
987 {
988 (void)&STL_SerialRegistrars::list_registrar<T1,T2>::registered;
989 AnyFixedRef tmp = data;
990 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
991 }
992
993 template<typename T1, typename T2>
serial_transform(SerialObject::elementList_t & serial,std::vector<T1,T2> & data,bool serialize)994 int serial_transform(SerialObject::elementList_t& serial,
995 std::vector<T1,T2>& data, bool serialize)
996 {
997 (void)&STL_SerialRegistrars::vector_registrar<T1,T2>::registered;
998 AnyFixedRef tmp = data;
999 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1000 }
1001
1002 template<typename T1, typename T2>
serial_transform(SerialObject::elementList_t & serial,std::deque<T1,T2> & data,bool serialize)1003 int serial_transform(SerialObject::elementList_t& serial,
1004 std::deque<T1,T2>& data, bool serialize)
1005 {
1006 (void)&STL_SerialRegistrars::deque_registrar<T1,T2>::registered;
1007 AnyFixedRef tmp = data;
1008 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1009 }
1010
1011 template<typename T1, typename T2>
serial_transform(SerialObject::elementList_t & serial,std::queue<T1,T2> & data,bool serialize)1012 int serial_transform(SerialObject::elementList_t& serial,
1013 std::queue<T1,T2>& data, bool serialize)
1014 {
1015 (void)&STL_SerialRegistrars::queue_registrar<T1,T2>::registered;
1016 AnyFixedRef tmp = data;
1017 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1018 }
1019
1020 template<typename T1, typename T2>
serial_transform(SerialObject::elementList_t & serial,std::stack<T1,T2> & data,bool serialize)1021 int serial_transform(SerialObject::elementList_t& serial,
1022 std::stack<T1,T2>& data, bool serialize)
1023 {
1024 (void)&STL_SerialRegistrars::stack_registrar<T1,T2>::registered;
1025 AnyFixedRef tmp = data;
1026 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1027 }
1028
1029 template<typename T1, typename T2, typename T3>
serial_transform(SerialObject::elementList_t & serial,std::priority_queue<T1,T2,T3> & data,bool serialize)1030 int serial_transform(SerialObject::elementList_t& serial,
1031 std::priority_queue<T1,T2,T3>& data, bool serialize)
1032 {
1033 (void)&STL_SerialRegistrars::priority_queue_registrar<T1,T2,T3>::registered;
1034 AnyFixedRef tmp = data;
1035 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1036 }
1037
1038 template<typename T1, typename T2, typename T3>
serial_transform(SerialObject::elementList_t & serial,std::set<T1,T2,T3> & data,bool serialize)1039 int serial_transform(SerialObject::elementList_t& serial,
1040 std::set<T1,T2,T3>& data, bool serialize)
1041 {
1042 (void)&STL_SerialRegistrars::set_registrar<T1,T2,T3>::registered;
1043 AnyFixedRef tmp = data;
1044 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1045 }
1046
1047 template<typename T1, typename T2, typename T3>
serial_transform(SerialObject::elementList_t & serial,std::multiset<T1,T2,T3> & data,bool serialize)1048 int serial_transform(SerialObject::elementList_t& serial,
1049 std::multiset<T1,T2,T3>& data, bool serialize)
1050 {
1051 (void)&STL_SerialRegistrars::multiset_registrar<T1,T2,T3>::registered;
1052 AnyFixedRef tmp = data;
1053 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1054 }
1055
1056 template<typename T1, typename T2, typename T3, typename T4>
serial_transform(SerialObject::elementList_t & serial,std::map<T1,T2,T3,T4> & data,bool serialize)1057 int serial_transform(SerialObject::elementList_t& serial,
1058 std::map<T1,T2,T3,T4>& data, bool serialize)
1059 {
1060 (void)&STL_SerialRegistrars::map_registrar<T1,T2,T3,T4>::registered;
1061 AnyFixedRef tmp = data;
1062 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1063 }
1064
1065 template<typename T1, typename T2, typename T3, typename T4>
serial_transform(SerialObject::elementList_t & serial,std::multimap<T1,T2,T3,T4> & data,bool serialize)1066 int serial_transform(SerialObject::elementList_t& serial,
1067 std::multimap<T1,T2,T3,T4>& data, bool serialize)
1068 {
1069 (void)&STL_SerialRegistrars::multimap_registrar<T1,T2,T3,T4>::registered;
1070 AnyFixedRef tmp = data;
1071 return Serializer().transform_impl(typeid(data), serial, tmp, serialize);
1072 }
1073
1074
1075 /** \param[in] data A reference to the variable to serialize
1076 * \param[in] pod_text_mode Indicate if POD data should be serialized
1077 * as a binary vector or a text-based vector (see utilib::XMLSerialStream).
1078 * \return The serialized object (as a SerialObject)
1079 *
1080 * This will take the object referenced by \a data and serialize it,
1081 * returning the resulting SerialObject. This function is a
1082 * convenience wrapper around serial_transform().
1083 */
1084 template<typename T>
1085 SerialObject Serialize(const T& data, bool pod_text_mode = false)
1086 {
1087 Serializer().set_pod_text_mode(pod_text_mode);
1088
1089 SerialObject::elementList_t tmp;
1090 int ans = serial_transform(tmp, const_cast<T&>(data), true);
1091 if ( ans != 0 )
1092 {
1093 EXCEPTION_MNGR(std::runtime_error, "Serialize(): "
1094 "Serialization failed for '"
1095 << typeid(T).name() << "' (Error " << ans << ")");
1096 }
1097 if ( tmp.empty() || ( ++(tmp.begin()) != tmp.end() ) )
1098 {
1099 EXCEPTION_MNGR(std::runtime_error, "Serialize(): "
1100 "[internal error] returned invalid object count for '"
1101 << typeid(T).name() << "'");
1102 }
1103 return tmp.front();
1104 }
1105
1106 /** \param[in] serial A serialized object
1107 * \return The expanded object specified by \a serial contained in an Any
1108 *
1109 * This will deserialize any SerialObject and return the expanded
1110 * object. This function is a convenience wrapper around
1111 * serial_transform().
1112 */
1113 Any Deserialize(SerialObject &serial);
1114
1115
1116 //-----------------------------------------------------------------------
1117 /** All serialization functions for standard STL classes.
1118 */
1119 namespace STL_Serializers {
1120
1121 template<typename T>
pair_serializer(SerialObject::elementList_t & serial,Any & data,bool serialize)1122 int pair_serializer( SerialObject::elementList_t& serial,
1123 Any& data, bool serialize )
1124 {
1125 T& tmp = const_cast<T&>(data.template expose<T>());
1126 int ans = serial_transform(serial, tmp.first, serialize);
1127 if ( ans != 0 )
1128 return ans;
1129 return serial_transform(serial, tmp.second, serialize);
1130 }
1131
1132
1133 template<typename T>
sequence_serializer(SerialObject::elementList_t & serial,Any & data,bool serialize)1134 int sequence_serializer( SerialObject::elementList_t& serial,
1135 Any& data, bool serialize )
1136 {
1137 T& tmp = const_cast<T&>(data.template expose<T>());
1138 if ( ! serialize )
1139 tmp.resize(serial.size());
1140
1141 int ans = 0;
1142 typename T::iterator it = tmp.begin();
1143 typename T::iterator itEnd = tmp.end();
1144 for ( ; it != itEnd; ++it )
1145 if ( 0 != (ans = serial_transform(serial, *it, serialize)) )
1146 return ans;
1147
1148 return 0;
1149 }
1150
1151 /// Helper class to gain access to protected sequence container in adaptor
1152 template<typename T>
1153 class ModifiedSequenceAdaptor : public T
1154 {
1155 public:
sub_transform(SerialObject::elementList_t & serial,bool serialize)1156 int sub_transform(SerialObject::elementList_t& serial, bool serialize)
1157 {
1158 return serial_transform(serial, T::c, serialize);
1159 }
1160 };
1161
1162 template<typename T>
adaptor_serializer(SerialObject::elementList_t & serial,Any & data,bool serialize)1163 int adaptor_serializer( SerialObject::elementList_t& serial,
1164 Any& data, bool serialize )
1165 {
1166 T& tmp = const_cast<T&>(data.template expose<T>());
1167 return reinterpret_cast<ModifiedSequenceAdaptor<T>&>(tmp)
1168 .sub_transform(serial, serialize);
1169 }
1170
1171
1172 template<typename T>
set_serializer(SerialObject::elementList_t & serial,Any & data,bool serialize)1173 int set_serializer( SerialObject::elementList_t& serial,
1174 Any& data, bool serialize )
1175 {
1176 int ans = 0;
1177 T& tmp = const_cast<T&>(data.template expose<T>());
1178 if ( serialize )
1179 {
1180 typename T::iterator it = tmp.begin();
1181 typename T::iterator itEnd = tmp.end();
1182 for ( ; it != itEnd; ++it )
1183 if ( 0 != (ans = serial_transform(serial, *it, serialize)) )
1184 return ans;
1185 }
1186 else
1187 {
1188 while ( ! serial.empty() )
1189 {
1190 AnyFixedRef i;
1191 ans = Serializer().transform_impl(typeid(void), serial, i, serialize);
1192 if ( ans != 0 )
1193 return ans;
1194 tmp.insert(tmp.end(), i.template expose<typename T::value_type>());
1195 }
1196 }
1197
1198 return 0;
1199 }
1200
1201 template<typename T>
map_serializer(SerialObject::elementList_t & serial,Any & data,bool serialize)1202 int map_serializer( SerialObject::elementList_t& serial,
1203 Any& data, bool serialize )
1204 {
1205 // Ideally, we would use the set_serializer above for all associative
1206 // containers. Unfortunately, the value_type for maps is
1207 // std::pair<const T1, T2>. As a result, we need to reinterpret it
1208 // as the non-const pair<T1,T2> BEFORE we stick it into an Any
1209 // (because the default "const T1" is not assignable and thus will
1210 // not compile).
1211 //
1212 // NB: This is further complicated because GCC deviates from the SGI
1213 // standard: SGI publishes T2 as map::data_type, while GCC uses
1214 // map::mapped_type. To get around this, we use
1215 // map::value_type::second_type (i.e. get T2 from the underlying
1216 // pair), which hopefully will be more portable...
1217 typedef std::pair<typename T::key_type, typename T::value_type::second_type>
1218 nonConstPair_t;
1219
1220 int ans = 0;
1221 T& tmp = const_cast<T&>(data.template expose<T>());
1222 if ( serialize )
1223 {
1224 typename T::iterator it = tmp.begin();
1225 typename T::iterator itEnd = tmp.end();
1226 for ( ; it != itEnd; ++it )
1227 {
1228 nonConstPair_t &element = reinterpret_cast<nonConstPair_t&>(*it);
1229 if ( 0 != (ans = serial_transform(serial, element, serialize)) )
1230 return ans;
1231 }
1232 }
1233 else
1234 {
1235 while ( ! serial.empty() )
1236 {
1237 AnyFixedRef i;
1238 ans = Serializer().transform_impl(typeid(void), serial, i, serialize);
1239 if ( ans != 0 )
1240 return ans;
1241 tmp.insert( tmp.end(), i.template expose<nonConstPair_t>() );
1242 }
1243 }
1244
1245 return 0;
1246 }
1247
1248 } // namespace STL_Serializers
1249
1250 } // namespace utilib
1251
1252 #endif // utilib_Serialize_h
1253