|
1 #ifndef BOOST_ARCHIVE_OSERIALIZER_HPP |
|
2 #define BOOST_ARCHIVE_OSERIALIZER_HPP |
|
3 |
|
4 // MS compatible compilers support #pragma once |
|
5 #if defined(_MSC_VER) && (_MSC_VER >= 1020) |
|
6 # pragma once |
|
7 #pragma inline_depth(511) |
|
8 #pragma inline_recursion(on) |
|
9 #endif |
|
10 |
|
11 #if defined(__MWERKS__) |
|
12 #pragma inline_depth(511) |
|
13 #endif |
|
14 |
|
15 /////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8 |
|
16 // oserializer.hpp: interface for serialization system. |
|
17 |
|
18 // (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . |
|
19 // Use, modification and distribution is subject to the Boost Software |
|
20 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
|
21 // http://www.boost.org/LICENSE_1_0.txt) |
|
22 |
|
23 // See http://www.boost.org for updates, documentation, and revision history. |
|
24 |
|
25 #include <cassert> |
|
26 |
|
27 #include <boost/config.hpp> |
|
28 #include <boost/detail/workaround.hpp> |
|
29 #include <boost/throw_exception.hpp> |
|
30 #include <boost/smart_cast.hpp> |
|
31 #include <boost/static_assert.hpp> |
|
32 #include <boost/static_warning.hpp> |
|
33 |
|
34 #include <boost/type_traits/is_pointer.hpp> |
|
35 #include <boost/type_traits/is_fundamental.hpp> |
|
36 #include <boost/type_traits/is_enum.hpp> |
|
37 #include <boost/type_traits/is_volatile.hpp> |
|
38 #include <boost/type_traits/is_const.hpp> |
|
39 #include <boost/type_traits/is_same.hpp> |
|
40 #include <boost/serialization/is_abstract.hpp> |
|
41 |
|
42 #include <boost/mpl/eval_if.hpp> |
|
43 #include <boost/mpl/and.hpp> |
|
44 #include <boost/mpl/less.hpp> |
|
45 #include <boost/mpl/greater_equal.hpp> |
|
46 #include <boost/mpl/equal_to.hpp> |
|
47 #include <boost/mpl/int.hpp> |
|
48 #include <boost/mpl/identity.hpp> |
|
49 #include <boost/mpl/list.hpp> |
|
50 #include <boost/mpl/empty.hpp> |
|
51 #include <boost/mpl/not.hpp> |
|
52 |
|
53 #ifndef BOOST_SERIALIZATION_DEFAULT_TYPE_INFO |
|
54 #include <boost/serialization/extended_type_info_typeid.hpp> |
|
55 #endif |
|
56 // the following is need only for dynamic cast of polymorphic pointers |
|
57 #include <boost/archive/detail/basic_oarchive.hpp> |
|
58 #include <boost/archive/detail/basic_oserializer.hpp> |
|
59 #include <boost/archive/detail/archive_pointer_oserializer.hpp> |
|
60 |
|
61 #include <boost/serialization/force_include.hpp> |
|
62 #include <boost/serialization/serialization.hpp> |
|
63 #include <boost/serialization/version.hpp> |
|
64 #include <boost/serialization/level.hpp> |
|
65 #include <boost/serialization/tracking.hpp> |
|
66 #include <boost/serialization/type_info_implementation.hpp> |
|
67 #include <boost/serialization/nvp.hpp> |
|
68 #include <boost/serialization/void_cast.hpp> |
|
69 |
|
70 #include <boost/archive/archive_exception.hpp> |
|
71 |
|
72 namespace boost { |
|
73 |
|
74 namespace serialization { |
|
75 class extended_type_info; |
|
76 } // namespace serialization |
|
77 |
|
78 namespace archive { |
|
79 |
|
80 // an accessor to permit friend access to archives. Needed because |
|
81 // some compilers don't handle friend templates completely |
|
82 class save_access { |
|
83 public: |
|
84 template<class Archive> |
|
85 static void end_preamble(Archive & ar){ |
|
86 ar.end_preamble(); |
|
87 } |
|
88 template<class Archive, class T> |
|
89 static void save_primitive(Archive & ar, const T & t){ |
|
90 ar.end_preamble(); |
|
91 ar.save(t); |
|
92 } |
|
93 }; |
|
94 |
|
95 namespace detail { |
|
96 |
|
97 template<class Archive, class T> |
|
98 class oserializer : public basic_oserializer |
|
99 { |
|
100 private: |
|
101 // private constructor to inhibit any existence other than the |
|
102 // static one |
|
103 explicit oserializer() : |
|
104 basic_oserializer( |
|
105 * boost::serialization::type_info_implementation<T>::type::get_instance() |
|
106 ) |
|
107 {} |
|
108 public: |
|
109 virtual BOOST_DLLEXPORT void save_object_data( |
|
110 basic_oarchive & ar, |
|
111 const void *x |
|
112 ) const BOOST_USED ; |
|
113 virtual bool class_info() const { |
|
114 return boost::serialization::implementation_level<T>::value |
|
115 >= boost::serialization::object_class_info; |
|
116 } |
|
117 virtual bool tracking(const unsigned int flags) const { |
|
118 // if(0 != (flags & no_tracking)) |
|
119 // return false; |
|
120 return boost::serialization::tracking_level<T>::value == boost::serialization::track_always |
|
121 || boost::serialization::tracking_level<T>::value == boost::serialization::track_selectivly |
|
122 && serialized_as_pointer(); |
|
123 } |
|
124 virtual unsigned int version() const { |
|
125 return ::boost::serialization::version<T>::value; |
|
126 } |
|
127 virtual bool is_polymorphic() const { |
|
128 typedef BOOST_DEDUCED_TYPENAME boost::serialization::type_info_implementation< |
|
129 T |
|
130 >::type::is_polymorphic::type typex; |
|
131 return typex::value; |
|
132 } |
|
133 static oserializer & instantiate(){ |
|
134 static oserializer instance; |
|
135 return instance; |
|
136 } |
|
137 virtual ~oserializer(){} |
|
138 }; |
|
139 |
|
140 template<class Archive, class T> |
|
141 BOOST_DLLEXPORT void oserializer<Archive, T>::save_object_data( |
|
142 basic_oarchive & ar, |
|
143 const void *x |
|
144 ) const { |
|
145 // make sure call is routed through the highest interface that might |
|
146 // be specialized by the user. |
|
147 boost::serialization::serialize_adl( |
|
148 boost::smart_cast_reference<Archive &>(ar), |
|
149 * static_cast<T *>(const_cast<void *>(x)), |
|
150 version() |
|
151 ); |
|
152 } |
|
153 |
|
154 // instantiation of this template creates a static object. Note inversion of |
|
155 // normal argument order to workaround bizarre error in MSVC 6.0 which only |
|
156 // manifests iftself during compiler time. |
|
157 template<class T, class Archive> |
|
158 class pointer_oserializer : public archive_pointer_oserializer<Archive> |
|
159 { |
|
160 private: |
|
161 virtual const basic_oserializer & get_basic_serializer() const { |
|
162 return oserializer<Archive, T>::instantiate(); |
|
163 } |
|
164 virtual BOOST_DLLEXPORT void save_object_ptr( |
|
165 basic_oarchive & ar, |
|
166 const void * x |
|
167 ) const BOOST_USED ; |
|
168 #if defined(__GNUC__) || ( defined(BOOST_MSVC) && (_MSC_VER <= 1300) ) |
|
169 public: |
|
170 #endif |
|
171 // private constructor to inhibit any existence other than the |
|
172 // static one. Note GCC doesn't permit constructor to be private |
|
173 explicit BOOST_DLLEXPORT pointer_oserializer() BOOST_USED; |
|
174 static const pointer_oserializer instance; |
|
175 public: |
|
176 #if ! BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) |
|
177 // at least one compiler (CW) seems to require that serialize_adl |
|
178 // be explicitly instantiated. Still under investigation. |
|
179 void (* const m)(Archive &, T &, const unsigned); |
|
180 boost::serialization::extended_type_info * (* e)(); |
|
181 #endif |
|
182 static BOOST_DLLEXPORT const pointer_oserializer & instantiate() BOOST_USED; |
|
183 virtual ~pointer_oserializer(){} |
|
184 }; |
|
185 |
|
186 template<class T, class Archive> |
|
187 BOOST_DLLEXPORT const pointer_oserializer<T, Archive> & |
|
188 pointer_oserializer<T, Archive>::instantiate(){ |
|
189 return instance; |
|
190 } |
|
191 |
|
192 // note: instances of this template to be constructed before the main |
|
193 // is called in order for things to be initialized properly. For this |
|
194 // reason, hiding the instance in a static function as was done above |
|
195 // won't work here so we created a free instance here. |
|
196 template<class T, class Archive> |
|
197 const pointer_oserializer<T, Archive> pointer_oserializer<T, Archive>::instance; |
|
198 |
|
199 template<class T, class Archive> |
|
200 BOOST_DLLEXPORT void pointer_oserializer<T, Archive>::save_object_ptr( |
|
201 basic_oarchive & ar, |
|
202 const void * x |
|
203 ) const { |
|
204 assert(NULL != x); |
|
205 // make sure call is routed through the highest interface that might |
|
206 // be specialized by the user. |
|
207 T * t = static_cast<T *>(const_cast<void *>(x)); |
|
208 const unsigned int file_version = boost::serialization::version<T>::value; |
|
209 Archive & ar_impl = boost::smart_cast_reference<Archive &>(ar); |
|
210 boost::serialization::save_construct_data_adl<Archive, T>( |
|
211 ar_impl, |
|
212 t, |
|
213 file_version |
|
214 ); |
|
215 ar_impl << boost::serialization::make_nvp(NULL, * t); |
|
216 } |
|
217 |
|
218 template<class T, class Archive> |
|
219 #if ! BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582)) |
|
220 BOOST_DLLEXPORT pointer_oserializer<T, Archive>::pointer_oserializer() : |
|
221 archive_pointer_oserializer<Archive>( |
|
222 * boost::serialization::type_info_implementation<T>::type::get_instance() |
|
223 ), |
|
224 m(boost::serialization::serialize_adl<Archive, T>), |
|
225 e(boost::serialization::type_info_implementation<T>::type::get_instance) |
|
226 #else |
|
227 BOOST_DLLEXPORT pointer_oserializer<T, Archive>::pointer_oserializer() : |
|
228 archive_pointer_oserializer<Archive>( |
|
229 * boost::serialization::type_info_implementation<T>::type::get_instance() |
|
230 ) |
|
231 #endif |
|
232 { |
|
233 // make sure appropriate member function is instantiated |
|
234 oserializer<Archive, T> & bos = oserializer<Archive, T>::instantiate(); |
|
235 bos.set_bpos(this); |
|
236 } |
|
237 |
|
238 template<class Archive, class T> |
|
239 struct save_non_pointer_type { |
|
240 // note this bounces the call right back to the archive |
|
241 // with no runtime overhead |
|
242 struct save_primitive { |
|
243 static void invoke(Archive & ar, const T & t){ |
|
244 save_access::save_primitive(ar, t); |
|
245 } |
|
246 }; |
|
247 // same as above but passes through serialization |
|
248 struct save_only { |
|
249 static void invoke(Archive & ar, const T & t){ |
|
250 // make sure call is routed through the highest interface that might |
|
251 // be specialized by the user. |
|
252 boost::serialization::serialize_adl( |
|
253 ar, |
|
254 const_cast<T &>(t), |
|
255 ::boost::serialization::version<T>::value |
|
256 ); |
|
257 } |
|
258 }; |
|
259 // adds class information to the archive. This includes |
|
260 // serialization level and class version |
|
261 struct save_standard { |
|
262 static void invoke(Archive &ar, const T & t){ |
|
263 ar.save_object(& t, oserializer<Archive, T>::instantiate()); |
|
264 } |
|
265 }; |
|
266 |
|
267 // adds class information to the archive. This includes |
|
268 // serialization level and class version |
|
269 struct save_conditional { |
|
270 static void invoke(Archive &ar, const T &t){ |
|
271 //if(0 == (ar.get_flags() & no_tracking)) |
|
272 save_standard::invoke(ar, t); |
|
273 //else |
|
274 // save_only::invoke(ar, t); |
|
275 } |
|
276 }; |
|
277 |
|
278 typedef |
|
279 BOOST_DEDUCED_TYPENAME mpl::eval_if< |
|
280 // if its primitive |
|
281 mpl::equal_to< |
|
282 boost::serialization::implementation_level<T>, |
|
283 mpl::int_<boost::serialization::primitive_type> |
|
284 >, |
|
285 mpl::identity<save_primitive>, |
|
286 // else |
|
287 BOOST_DEDUCED_TYPENAME mpl::eval_if< |
|
288 // class info / version |
|
289 mpl::greater_equal< |
|
290 boost::serialization::implementation_level<T>, |
|
291 mpl::int_<boost::serialization::object_class_info> |
|
292 >, |
|
293 // do standard save |
|
294 mpl::identity<save_standard>, |
|
295 // else |
|
296 BOOST_DEDUCED_TYPENAME mpl::eval_if< |
|
297 // no tracking |
|
298 mpl::equal_to< |
|
299 boost::serialization::tracking_level<T>, |
|
300 mpl::int_<boost::serialization::track_never> |
|
301 >, |
|
302 // do a fast save |
|
303 mpl::identity<save_only>, |
|
304 // else |
|
305 // do a fast save only tracking is turned off |
|
306 mpl::identity<save_conditional> |
|
307 > > >::type typex; |
|
308 |
|
309 static void invoke(Archive & ar, const T & t){ |
|
310 // check that we're not trying to serialize something that |
|
311 // has been marked not to be serialized. If this your program |
|
312 // traps here, you've tried to serialize a class whose trait |
|
313 // has been marked "non-serializable". Either reset the trait |
|
314 // (see level.hpp) or change program not to serialize items of this class |
|
315 BOOST_STATIC_ASSERT(( |
|
316 mpl::greater_equal< |
|
317 boost::serialization::implementation_level<T>, |
|
318 mpl::int_<boost::serialization::primitive_type> |
|
319 >::value |
|
320 )); |
|
321 typex::invoke(ar, t); |
|
322 }; |
|
323 }; |
|
324 |
|
325 template<class Archive, class TPtr> |
|
326 struct save_pointer_type { |
|
327 template<class T> |
|
328 struct abstract |
|
329 { |
|
330 static const basic_pointer_oserializer * register_type(Archive & /* ar */){ |
|
331 // it has? to be polymorphic |
|
332 BOOST_STATIC_ASSERT( |
|
333 boost::serialization::type_info_implementation<T>::type::is_polymorphic::value |
|
334 ); |
|
335 return static_cast<const basic_pointer_oserializer *>(NULL); |
|
336 } |
|
337 }; |
|
338 |
|
339 template<class T> |
|
340 struct non_abstract |
|
341 { |
|
342 static const basic_pointer_oserializer * register_type(Archive & ar){ |
|
343 return ar.register_type(static_cast<T *>(NULL)); |
|
344 } |
|
345 }; |
|
346 |
|
347 template<class T> |
|
348 static const basic_pointer_oserializer * register_type(Archive &ar, T & /*t*/){ |
|
349 // there should never be any need to save an abstract polymorphic |
|
350 // class pointer. Inhibiting code generation for this |
|
351 // permits abstract base classes to be used - note: exception |
|
352 // virtual serialize functions used for plug-ins |
|
353 typedef |
|
354 BOOST_DEDUCED_TYPENAME mpl::eval_if< |
|
355 serialization::is_abstract<T>, |
|
356 mpl::identity<abstract<T> >, |
|
357 mpl::identity<non_abstract<T> > |
|
358 >::type typex; |
|
359 return typex::register_type(ar); |
|
360 } |
|
361 |
|
362 template<class T> |
|
363 struct non_polymorphic |
|
364 { |
|
365 static void save( |
|
366 Archive &ar, |
|
367 const T & t, |
|
368 const basic_pointer_oserializer * bpos_ptr |
|
369 ){ |
|
370 // save the requested pointer type |
|
371 ar.save_pointer(& t, bpos_ptr); |
|
372 } |
|
373 }; |
|
374 |
|
375 template<class T> |
|
376 struct polymorphic |
|
377 { |
|
378 static void save( |
|
379 Archive &ar, |
|
380 const T & t, |
|
381 const basic_pointer_oserializer * bpos_ptr |
|
382 ){ |
|
383 const boost::serialization::extended_type_info * this_type |
|
384 = boost::serialization::type_info_implementation<T>::type::get_instance(); |
|
385 // retrieve the true type of the object pointed to |
|
386 // if this assertion fails its an error in this library |
|
387 assert(NULL != this_type); |
|
388 const boost::serialization::extended_type_info * true_type |
|
389 = boost::serialization::type_info_implementation<T>::type::get_derived_extended_type_info(t); |
|
390 // note:if this exception is thrown, be sure that derived pointer |
|
391 // is either regsitered or exported. |
|
392 if(NULL == true_type){ |
|
393 boost::throw_exception( |
|
394 archive_exception(archive_exception::unregistered_class) |
|
395 ); |
|
396 } |
|
397 |
|
398 // if its not a pointer to a more derived type |
|
399 const void *vp = static_cast<const void *>(&t); |
|
400 if(*this_type == *true_type){ |
|
401 ar.save_pointer(vp, bpos_ptr); |
|
402 return; |
|
403 } |
|
404 // convert pointer to more derived type. if this is thrown |
|
405 // it means that the base/derived relationship hasn't be registered |
|
406 vp = serialization::void_downcast(*true_type, *this_type, &t); |
|
407 if(NULL == vp){ |
|
408 boost::throw_exception( |
|
409 archive_exception(archive_exception::unregistered_cast) |
|
410 ); |
|
411 } |
|
412 |
|
413 // sice true_type is valid, and this only gets made if the |
|
414 // pointer oserializer object has been created, this should never |
|
415 // fail |
|
416 bpos_ptr = archive_pointer_oserializer<Archive>::find(* true_type); |
|
417 assert(NULL != bpos_ptr); |
|
418 if(NULL == bpos_ptr) |
|
419 boost::throw_exception( |
|
420 archive_exception(archive_exception::unregistered_class) |
|
421 ); |
|
422 ar.save_pointer(vp, bpos_ptr); |
|
423 } |
|
424 }; |
|
425 |
|
426 template<class T> |
|
427 static void save( |
|
428 Archive & ar, |
|
429 const T &t, |
|
430 const basic_pointer_oserializer * bpos_ptr |
|
431 ){ |
|
432 typedef BOOST_DEDUCED_TYPENAME mpl::eval_if< |
|
433 BOOST_DEDUCED_TYPENAME boost::serialization:: |
|
434 type_info_implementation<T>::type::is_polymorphic, |
|
435 mpl::identity<polymorphic<T> >, |
|
436 mpl::identity<non_polymorphic<T> > |
|
437 >::type typey; |
|
438 typey::save(ar, const_cast<T &>(t), bpos_ptr); |
|
439 } |
|
440 |
|
441 template<class T> |
|
442 static void const_check(T & t){ |
|
443 BOOST_STATIC_ASSERT(! boost::is_const<T>::value); |
|
444 } |
|
445 |
|
446 static void invoke(Archive &ar, const TPtr t){ |
|
447 #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION |
|
448 // if your program traps here, its because you tried to do |
|
449 // something like ar << t where t is a pointer to a const value |
|
450 // void f3(A const* a, text_oarchive& oa) |
|
451 // { |
|
452 // oa << a; |
|
453 // } |
|
454 // with a compiler which doesn't support remove_const |
|
455 // const_check(* t); |
|
456 #else |
|
457 // otherwise remove the const |
|
458 #endif |
|
459 const basic_pointer_oserializer * bpos_ptr = register_type(ar, * t); |
|
460 if(NULL == t){ |
|
461 basic_oarchive & boa = boost::smart_cast_reference<basic_oarchive &>(ar); |
|
462 boa.save_null_pointer(); |
|
463 save_access::end_preamble(ar); |
|
464 return; |
|
465 } |
|
466 save(ar, * t, bpos_ptr); |
|
467 }; |
|
468 }; |
|
469 |
|
470 template<class Archive, class T> |
|
471 struct save_enum_type |
|
472 { |
|
473 static void invoke(Archive &ar, const T &t){ |
|
474 // convert enum to integers on save |
|
475 const int i = static_cast<int>(t); |
|
476 ar << boost::serialization::make_nvp(NULL, i); |
|
477 } |
|
478 }; |
|
479 |
|
480 template<class Archive, class T> |
|
481 struct save_array_type |
|
482 { |
|
483 static void invoke(Archive &ar, const T &t){ |
|
484 save_access::end_preamble(ar); |
|
485 // consider alignment |
|
486 int count = sizeof(t) / ( |
|
487 static_cast<const char *>(static_cast<const void *>(&t[1])) |
|
488 - static_cast<const char *>(static_cast<const void *>(&t[0])) |
|
489 ); |
|
490 ar << BOOST_SERIALIZATION_NVP(count); |
|
491 int i; |
|
492 for(i = 0; i < count; ++i) |
|
493 ar << boost::serialization::make_nvp("item", t[i]); |
|
494 } |
|
495 }; |
|
496 |
|
497 // note bogus arguments to workaround msvc 6 silent runtime failure |
|
498 // declaration to satisfy gcc |
|
499 template<class Archive, class T> |
|
500 BOOST_DLLEXPORT const basic_pointer_oserializer & |
|
501 instantiate_pointer_oserializer( |
|
502 Archive * /* ar = NULL */, |
|
503 T * /* t = NULL */ |
|
504 ) BOOST_USED ; |
|
505 // definition |
|
506 template<class Archive, class T> |
|
507 BOOST_DLLEXPORT const basic_pointer_oserializer & |
|
508 instantiate_pointer_oserializer( |
|
509 Archive * /* ar = NULL */, |
|
510 T * /* t = NULL */ |
|
511 ){ |
|
512 // note: reversal of order of arguments to work around msvc 6.0 bug |
|
513 // that manifests itself while trying to link. |
|
514 return pointer_oserializer<T, Archive>::instantiate(); |
|
515 } |
|
516 |
|
517 } // detail |
|
518 |
|
519 template<class Archive, class T> |
|
520 inline void save(Archive & ar, const T &t){ |
|
521 typedef |
|
522 BOOST_DEDUCED_TYPENAME mpl::eval_if<is_pointer<T>, |
|
523 mpl::identity<detail::save_pointer_type<Archive, T> >, |
|
524 //else |
|
525 BOOST_DEDUCED_TYPENAME mpl::eval_if<is_enum<T>, |
|
526 mpl::identity<detail::save_enum_type<Archive, T> >, |
|
527 //else |
|
528 BOOST_DEDUCED_TYPENAME mpl::eval_if<is_array<T>, |
|
529 mpl::identity<detail::save_array_type<Archive, T> >, |
|
530 //else |
|
531 mpl::identity<detail::save_non_pointer_type<Archive, T> > |
|
532 > |
|
533 > |
|
534 >::type typex; |
|
535 typex::invoke(ar, t); |
|
536 } |
|
537 |
|
538 #ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING |
|
539 |
|
540 template<class T> |
|
541 struct check_tracking { |
|
542 typedef BOOST_DEDUCED_TYPENAME mpl::if_< |
|
543 // if its never tracked. |
|
544 BOOST_DEDUCED_TYPENAME mpl::equal_to< |
|
545 serialization::tracking_level<T>, |
|
546 mpl::int_<serialization::track_never> |
|
547 >, |
|
548 // it better not be a pointer |
|
549 mpl::not_<is_pointer<T> >, |
|
550 //else |
|
551 // otherwise if it might be tracked. So there shouldn't |
|
552 // be any problem making a const |
|
553 is_const<T> |
|
554 >::type typex; |
|
555 BOOST_STATIC_CONSTANT(bool, value = typex::value); |
|
556 }; |
|
557 |
|
558 template<class Archive, class T> |
|
559 inline void save(Archive & ar, T &t){ |
|
560 // if your program traps here, it indicates taht your doing one of the following: |
|
561 // a) serializing an object of a type marked "track_never" through a pointer. |
|
562 // b) saving an non-const object of a type not markd "track_never) |
|
563 // Either of these conditions may be an indicator of an error usage of the |
|
564 // serialization library and should be double checked. See documentation on |
|
565 // object tracking. |
|
566 BOOST_STATIC_ASSERT(check_tracking<T>::value); |
|
567 save(ar, const_cast<const T &>(t)); |
|
568 } |
|
569 #endif |
|
570 |
|
571 } // namespace archive |
|
572 } // namespace boost |
|
573 |
|
574 #endif // BOOST_ARCHIVE_OSERIALIZER_HPP |