]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #ifndef BOOST_ARCHIVE_DETAIL_ISERIALIZER_HPP |
2 | #define BOOST_ARCHIVE_DETAIL_ISERIALIZER_HPP | |
3 | ||
4 | // MS compatible compilers support #pragma once | |
5 | #if defined(_MSC_VER) | |
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 | // iserializer.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 <new> // for placement new | |
26 | #include <cstddef> // size_t, NULL | |
27 | ||
28 | #include <boost/config.hpp> | |
29 | #include <boost/detail/workaround.hpp> | |
30 | #if defined(BOOST_NO_STDC_NAMESPACE) | |
31 | namespace std{ | |
32 | using ::size_t; | |
33 | } // namespace std | |
34 | #endif | |
35 | ||
36 | #include <boost/static_assert.hpp> | |
37 | ||
38 | #include <boost/mpl/eval_if.hpp> | |
39 | #include <boost/mpl/identity.hpp> | |
40 | #include <boost/mpl/greater_equal.hpp> | |
41 | #include <boost/mpl/equal_to.hpp> | |
42 | #include <boost/core/no_exceptions_support.hpp> | |
43 | ||
44 | #ifndef BOOST_SERIALIZATION_DEFAULT_TYPE_INFO | |
45 | #include <boost/serialization/extended_type_info_typeid.hpp> | |
46 | #endif | |
47 | #include <boost/serialization/throw_exception.hpp> | |
48 | #include <boost/serialization/smart_cast.hpp> | |
49 | #include <boost/serialization/static_warning.hpp> | |
50 | ||
51 | #include <boost/type_traits/is_pointer.hpp> | |
52 | #include <boost/type_traits/is_enum.hpp> | |
53 | #include <boost/type_traits/is_const.hpp> | |
54 | #include <boost/type_traits/remove_const.hpp> | |
55 | #include <boost/type_traits/remove_extent.hpp> | |
56 | #include <boost/type_traits/is_polymorphic.hpp> | |
57 | ||
58 | #include <boost/serialization/assume_abstract.hpp> | |
59 | ||
60 | #ifndef BOOST_MSVC | |
61 | #define DONT_USE_HAS_NEW_OPERATOR ( \ | |
62 | BOOST_WORKAROUND(__IBMCPP__, < 1210) \ | |
63 | || defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x590) \ | |
64 | ) | |
65 | #else | |
66 | #define DONT_USE_HAS_NEW_OPERATOR 0 | |
67 | #endif | |
68 | ||
69 | #if ! DONT_USE_HAS_NEW_OPERATOR | |
70 | #include <boost/type_traits/has_new_operator.hpp> | |
71 | #endif | |
72 | ||
73 | #include <boost/serialization/serialization.hpp> | |
74 | #include <boost/serialization/version.hpp> | |
75 | #include <boost/serialization/level.hpp> | |
76 | #include <boost/serialization/tracking.hpp> | |
77 | #include <boost/serialization/type_info_implementation.hpp> | |
78 | #include <boost/serialization/nvp.hpp> | |
79 | #include <boost/serialization/void_cast.hpp> | |
80 | #include <boost/serialization/array.hpp> | |
81 | #include <boost/serialization/collection_size_type.hpp> | |
82 | #include <boost/serialization/singleton.hpp> | |
83 | #include <boost/serialization/wrapper.hpp> | |
84 | ||
85 | // the following is need only for dynamic cast of polymorphic pointers | |
86 | #include <boost/archive/archive_exception.hpp> | |
87 | #include <boost/archive/detail/basic_iarchive.hpp> | |
88 | #include <boost/archive/detail/basic_iserializer.hpp> | |
89 | #include <boost/archive/detail/basic_pointer_iserializer.hpp> | |
90 | #include <boost/archive/detail/archive_serializer_map.hpp> | |
91 | #include <boost/archive/detail/check.hpp> | |
92 | ||
93 | namespace boost { | |
94 | ||
95 | namespace serialization { | |
96 | class extended_type_info; | |
97 | } // namespace serialization | |
98 | ||
99 | namespace archive { | |
100 | ||
101 | // an accessor to permit friend access to archives. Needed because | |
102 | // some compilers don't handle friend templates completely | |
103 | class load_access { | |
104 | public: | |
105 | template<class Archive, class T> | |
106 | static void load_primitive(Archive &ar, T &t){ | |
107 | ar.load(t); | |
108 | } | |
109 | }; | |
110 | ||
111 | namespace detail { | |
112 | ||
113 | #ifdef BOOST_MSVC | |
114 | # pragma warning(push) | |
115 | # pragma warning(disable : 4511 4512) | |
116 | #endif | |
117 | ||
118 | template<class Archive, class T> | |
119 | class iserializer : public basic_iserializer | |
120 | { | |
121 | private: | |
122 | virtual void destroy(/*const*/ void *address) const { | |
123 | boost::serialization::access::destroy(static_cast<T *>(address)); | |
124 | } | |
125 | protected: | |
126 | // protected constructor since it's always created by singleton | |
127 | explicit iserializer() : | |
128 | basic_iserializer( | |
129 | boost::serialization::singleton< | |
130 | typename | |
131 | boost::serialization::type_info_implementation< T >::type | |
132 | >::get_const_instance() | |
133 | ) | |
134 | {} | |
135 | public: | |
136 | virtual BOOST_DLLEXPORT void load_object_data( | |
137 | basic_iarchive & ar, | |
138 | void *x, | |
139 | const unsigned int file_version | |
140 | ) const BOOST_USED; | |
141 | virtual bool class_info() const { | |
142 | return boost::serialization::implementation_level< T >::value | |
143 | >= boost::serialization::object_class_info; | |
144 | } | |
145 | virtual bool tracking(const unsigned int /* flags */) const { | |
146 | return boost::serialization::tracking_level< T >::value | |
147 | == boost::serialization::track_always | |
148 | || ( boost::serialization::tracking_level< T >::value | |
149 | == boost::serialization::track_selectively | |
150 | && serialized_as_pointer()); | |
151 | } | |
152 | virtual version_type version() const { | |
153 | return version_type(::boost::serialization::version< T >::value); | |
154 | } | |
155 | virtual bool is_polymorphic() const { | |
156 | return boost::is_polymorphic< T >::value; | |
157 | } | |
158 | virtual ~iserializer(){}; | |
159 | }; | |
160 | ||
161 | #ifdef BOOST_MSVC | |
162 | # pragma warning(pop) | |
163 | #endif | |
164 | ||
165 | template<class Archive, class T> | |
166 | BOOST_DLLEXPORT void iserializer<Archive, T>::load_object_data( | |
167 | basic_iarchive & ar, | |
168 | void *x, | |
169 | const unsigned int file_version | |
170 | ) const { | |
171 | // note: we now comment this out. Before we permited archive | |
172 | // version # to be very large. Now we don't. To permit | |
173 | // readers of these old archives, we have to suppress this | |
174 | // code. Perhaps in the future we might re-enable it but | |
175 | // permit its suppression with a runtime switch. | |
176 | #if 0 | |
177 | // trap case where the program cannot handle the current version | |
178 | if(file_version > static_cast<const unsigned int>(version())) | |
179 | boost::serialization::throw_exception( | |
180 | archive::archive_exception( | |
181 | boost::archive::archive_exception::unsupported_class_version, | |
182 | get_debug_info() | |
183 | ) | |
184 | ); | |
185 | #endif | |
186 | // make sure call is routed through the higest interface that might | |
187 | // be specialized by the user. | |
188 | boost::serialization::serialize_adl( | |
189 | boost::serialization::smart_cast_reference<Archive &>(ar), | |
190 | * static_cast<T *>(x), | |
191 | file_version | |
192 | ); | |
193 | } | |
194 | ||
195 | #ifdef BOOST_MSVC | |
196 | # pragma warning(push) | |
197 | # pragma warning(disable : 4511 4512) | |
198 | #endif | |
199 | ||
200 | // the purpose of this code is to allocate memory for an object | |
201 | // without requiring the constructor to be called. Presumably | |
202 | // the allocated object will be subsequently initialized with | |
203 | // "placement new". | |
204 | // note: we have the boost type trait has_new_operator but we | |
205 | // have no corresponding has_delete_operator. So we presume | |
206 | // that the former being true would imply that the a delete | |
207 | // operator is also defined for the class T. | |
208 | ||
209 | template<class T> | |
210 | struct heap_allocation { | |
211 | // boost::has_new_operator< T > doesn't work on these compilers | |
212 | #if DONT_USE_HAS_NEW_OPERATOR | |
213 | // This doesn't handle operator new overload for class T | |
214 | static T * invoke_new(){ | |
215 | return static_cast<T *>(operator new(sizeof(T))); | |
216 | } | |
217 | static void invoke_delete(T *t){ | |
218 | (operator delete(t)); | |
219 | } | |
220 | #else | |
221 | // note: we presume that a true value for has_new_operator | |
222 | // implies the existence of a class specific delete operator as well | |
223 | // as a class specific new operator. | |
224 | struct has_new_operator { | |
225 | static T * invoke_new() { | |
226 | return static_cast<T *>((T::operator new)(sizeof(T))); | |
227 | } | |
228 | static void invoke_delete(T * t) { | |
229 | // if compilation fails here, the likely cause that the class | |
230 | // T has a class specific new operator but no class specific | |
231 | // delete operator which matches the following signature. | |
232 | // note that this solution addresses the issue that two | |
233 | // possible signatures. But it doesn't address the possibility | |
234 | // that the class might have class specific new with NO | |
235 | // class specific delete at all. Patches (compatible with | |
236 | // C++03) welcome! | |
237 | delete t; | |
238 | } | |
239 | }; | |
240 | struct doesnt_have_new_operator { | |
241 | static T* invoke_new() { | |
242 | return static_cast<T *>(operator new(sizeof(T))); | |
243 | } | |
244 | static void invoke_delete(T * t) { | |
245 | // Note: I'm reliance upon automatic conversion from T * to void * here | |
246 | delete t; | |
247 | } | |
248 | }; | |
249 | static T * invoke_new() { | |
250 | typedef typename | |
251 | mpl::eval_if< | |
252 | boost::has_new_operator< T >, | |
253 | mpl::identity<has_new_operator >, | |
254 | mpl::identity<doesnt_have_new_operator > | |
255 | >::type typex; | |
256 | return typex::invoke_new(); | |
257 | } | |
258 | static void invoke_delete(T *t) { | |
259 | typedef typename | |
260 | mpl::eval_if< | |
261 | boost::has_new_operator< T >, | |
262 | mpl::identity<has_new_operator >, | |
263 | mpl::identity<doesnt_have_new_operator > | |
264 | >::type typex; | |
265 | typex::invoke_delete(t); | |
266 | } | |
267 | #endif | |
268 | explicit heap_allocation(){ | |
269 | m_p = invoke_new(); | |
270 | } | |
271 | ~heap_allocation(){ | |
272 | if (0 != m_p) | |
273 | invoke_delete(m_p); | |
274 | } | |
275 | T* get() const { | |
276 | return m_p; | |
277 | } | |
278 | ||
279 | T* release() { | |
280 | T* p = m_p; | |
281 | m_p = 0; | |
282 | return p; | |
283 | } | |
284 | private: | |
285 | T* m_p; | |
286 | }; | |
287 | ||
288 | template<class Archive, class T> | |
289 | class pointer_iserializer : | |
290 | public basic_pointer_iserializer | |
291 | { | |
292 | private: | |
293 | virtual void * heap_allocation() const { | |
294 | detail::heap_allocation<T> h; | |
295 | T * t = h.get(); | |
296 | h.release(); | |
297 | return t; | |
298 | } | |
299 | virtual const basic_iserializer & get_basic_serializer() const { | |
300 | return boost::serialization::singleton< | |
301 | iserializer<Archive, T> | |
302 | >::get_const_instance(); | |
303 | } | |
304 | BOOST_DLLEXPORT virtual void load_object_ptr( | |
305 | basic_iarchive & ar, | |
306 | void * x, | |
307 | const unsigned int file_version | |
308 | ) const BOOST_USED; | |
309 | protected: | |
310 | // this should alway be a singleton so make the constructor protected | |
311 | pointer_iserializer(); | |
312 | ~pointer_iserializer(); | |
313 | }; | |
314 | ||
315 | #ifdef BOOST_MSVC | |
316 | # pragma warning(pop) | |
317 | #endif | |
318 | ||
319 | // note: BOOST_DLLEXPORT is so that code for polymorphic class | |
320 | // serialized only through base class won't get optimized out | |
321 | template<class Archive, class T> | |
322 | BOOST_DLLEXPORT void pointer_iserializer<Archive, T>::load_object_ptr( | |
323 | basic_iarchive & ar, | |
324 | void * t, | |
325 | const unsigned int file_version | |
326 | ) const | |
327 | { | |
328 | Archive & ar_impl = | |
329 | boost::serialization::smart_cast_reference<Archive &>(ar); | |
330 | ||
331 | // note that the above will throw std::bad_alloc if the allocation | |
332 | // fails so we don't have to address this contingency here. | |
333 | ||
334 | // catch exception during load_construct_data so that we don't | |
335 | // automatically delete the t which is most likely not fully | |
336 | // constructed | |
337 | BOOST_TRY { | |
338 | // this addresses an obscure situation that occurs when | |
339 | // load_constructor de-serializes something through a pointer. | |
340 | ar.next_object_pointer(t); | |
341 | boost::serialization::load_construct_data_adl<Archive, T>( | |
342 | ar_impl, | |
343 | static_cast<T *>(t), | |
344 | file_version | |
345 | ); | |
346 | } | |
347 | BOOST_CATCH(...){ | |
348 | // if we get here the load_construct failed. The heap_allocation | |
349 | // will be automatically deleted so we don't have to do anything | |
350 | // special here. | |
351 | BOOST_RETHROW; | |
352 | } | |
353 | BOOST_CATCH_END | |
354 | ||
355 | ar_impl >> boost::serialization::make_nvp(NULL, * static_cast<T *>(t)); | |
356 | } | |
357 | ||
358 | template<class Archive, class T> | |
359 | pointer_iserializer<Archive, T>::pointer_iserializer() : | |
360 | basic_pointer_iserializer( | |
361 | boost::serialization::singleton< | |
362 | typename | |
363 | boost::serialization::type_info_implementation< T >::type | |
364 | >::get_const_instance() | |
365 | ) | |
366 | { | |
367 | boost::serialization::singleton< | |
368 | iserializer<Archive, T> | |
369 | >::get_mutable_instance().set_bpis(this); | |
370 | archive_serializer_map<Archive>::insert(this); | |
371 | } | |
372 | ||
373 | template<class Archive, class T> | |
374 | pointer_iserializer<Archive, T>::~pointer_iserializer(){ | |
375 | archive_serializer_map<Archive>::erase(this); | |
376 | } | |
377 | ||
378 | template<class Archive> | |
379 | struct load_non_pointer_type { | |
380 | // note this bounces the call right back to the archive | |
381 | // with no runtime overhead | |
382 | struct load_primitive { | |
383 | template<class T> | |
384 | static void invoke(Archive & ar, T & t){ | |
385 | load_access::load_primitive(ar, t); | |
386 | } | |
387 | }; | |
388 | // note this bounces the call right back to the archive | |
389 | // with no runtime overhead | |
390 | struct load_only { | |
391 | template<class T> | |
392 | static void invoke(Archive & ar, const T & t){ | |
393 | // short cut to user's serializer | |
394 | // make sure call is routed through the higest interface that might | |
395 | // be specialized by the user. | |
396 | boost::serialization::serialize_adl( | |
397 | ar, | |
398 | const_cast<T &>(t), | |
399 | boost::serialization::version< T >::value | |
400 | ); | |
401 | } | |
402 | }; | |
403 | ||
404 | // note this save class information including version | |
405 | // and serialization level to the archive | |
406 | struct load_standard { | |
407 | template<class T> | |
408 | static void invoke(Archive &ar, const T & t){ | |
409 | void * x = & const_cast<T &>(t); | |
410 | ar.load_object( | |
411 | x, | |
412 | boost::serialization::singleton< | |
413 | iserializer<Archive, T> | |
414 | >::get_const_instance() | |
415 | ); | |
416 | } | |
417 | }; | |
418 | ||
419 | struct load_conditional { | |
420 | template<class T> | |
421 | static void invoke(Archive &ar, T &t){ | |
422 | //if(0 == (ar.get_flags() & no_tracking)) | |
423 | load_standard::invoke(ar, t); | |
424 | //else | |
425 | // load_only::invoke(ar, t); | |
426 | } | |
427 | }; | |
428 | ||
429 | template<class T> | |
430 | static void invoke(Archive & ar, T &t){ | |
431 | typedef typename mpl::eval_if< | |
432 | // if its primitive | |
433 | mpl::equal_to< | |
434 | boost::serialization::implementation_level< T >, | |
435 | mpl::int_<boost::serialization::primitive_type> | |
436 | >, | |
437 | mpl::identity<load_primitive>, | |
438 | // else | |
439 | typename mpl::eval_if< | |
440 | // class info / version | |
441 | mpl::greater_equal< | |
442 | boost::serialization::implementation_level< T >, | |
443 | mpl::int_<boost::serialization::object_class_info> | |
444 | >, | |
445 | // do standard load | |
446 | mpl::identity<load_standard>, | |
447 | // else | |
448 | typename mpl::eval_if< | |
449 | // no tracking | |
450 | mpl::equal_to< | |
451 | boost::serialization::tracking_level< T >, | |
452 | mpl::int_<boost::serialization::track_never> | |
453 | >, | |
454 | // do a fast load | |
455 | mpl::identity<load_only>, | |
456 | // else | |
457 | // do a fast load only tracking is turned off | |
458 | mpl::identity<load_conditional> | |
459 | > > >::type typex; | |
460 | check_object_versioning< T >(); | |
461 | check_object_level< T >(); | |
462 | typex::invoke(ar, t); | |
463 | } | |
464 | }; | |
465 | ||
466 | template<class Archive> | |
467 | struct load_pointer_type { | |
468 | struct abstract | |
469 | { | |
470 | template<class T> | |
471 | static const basic_pointer_iserializer * register_type(Archive & /* ar */){ | |
472 | // it has? to be polymorphic | |
473 | BOOST_STATIC_ASSERT(boost::is_polymorphic< T >::value); | |
474 | return static_cast<basic_pointer_iserializer *>(NULL); | |
475 | } | |
476 | }; | |
477 | ||
478 | struct non_abstract | |
479 | { | |
480 | template<class T> | |
481 | static const basic_pointer_iserializer * register_type(Archive & ar){ | |
482 | return ar.register_type(static_cast<T *>(NULL)); | |
483 | } | |
484 | }; | |
485 | ||
486 | template<class T> | |
487 | static const basic_pointer_iserializer * register_type(Archive &ar, const T & /*t*/){ | |
488 | // there should never be any need to load an abstract polymorphic | |
489 | // class pointer. Inhibiting code generation for this | |
490 | // permits abstract base classes to be used - note: exception | |
491 | // virtual serialize functions used for plug-ins | |
492 | typedef typename | |
493 | mpl::eval_if< | |
494 | boost::serialization::is_abstract<const T>, | |
495 | boost::mpl::identity<abstract>, | |
496 | boost::mpl::identity<non_abstract> | |
497 | >::type typex; | |
498 | return typex::template register_type< T >(ar); | |
499 | } | |
500 | ||
501 | template<class T> | |
502 | static T * pointer_tweak( | |
503 | const boost::serialization::extended_type_info & eti, | |
504 | void const * const t, | |
505 | const T & | |
506 | ) { | |
507 | // tweak the pointer back to the base class | |
508 | void * upcast = const_cast<void *>( | |
509 | boost::serialization::void_upcast( | |
510 | eti, | |
511 | boost::serialization::singleton< | |
512 | typename | |
513 | boost::serialization::type_info_implementation< T >::type | |
514 | >::get_const_instance(), | |
515 | t | |
516 | ) | |
517 | ); | |
518 | if(NULL == upcast) | |
519 | boost::serialization::throw_exception( | |
520 | archive_exception(archive_exception::unregistered_class) | |
521 | ); | |
522 | return static_cast<T *>(upcast); | |
523 | } | |
524 | ||
525 | template<class T> | |
526 | static void check_load(T & /* t */){ | |
527 | check_pointer_level< T >(); | |
528 | check_pointer_tracking< T >(); | |
529 | } | |
530 | ||
531 | static const basic_pointer_iserializer * | |
532 | find(const boost::serialization::extended_type_info & type){ | |
533 | return static_cast<const basic_pointer_iserializer *>( | |
534 | archive_serializer_map<Archive>::find(type) | |
535 | ); | |
536 | } | |
537 | ||
538 | template<class Tptr> | |
539 | static void invoke(Archive & ar, Tptr & t){ | |
540 | check_load(*t); | |
541 | const basic_pointer_iserializer * bpis_ptr = register_type(ar, *t); | |
542 | const basic_pointer_iserializer * newbpis_ptr = ar.load_pointer( | |
543 | // note major hack here !!! | |
544 | // I tried every way to convert Tptr &t (where Tptr might | |
545 | // include const) to void * &. This is the only way | |
546 | // I could make it work. RR | |
547 | (void * & )t, | |
548 | bpis_ptr, | |
549 | find | |
550 | ); | |
551 | // if the pointer isn't that of the base class | |
552 | if(newbpis_ptr != bpis_ptr){ | |
553 | t = pointer_tweak(newbpis_ptr->get_eti(), t, *t); | |
554 | } | |
555 | } | |
556 | }; | |
557 | ||
558 | template<class Archive> | |
559 | struct load_enum_type { | |
560 | template<class T> | |
561 | static void invoke(Archive &ar, T &t){ | |
562 | // convert integers to correct enum to load | |
563 | int i; | |
564 | ar >> boost::serialization::make_nvp(NULL, i); | |
565 | t = static_cast< T >(i); | |
566 | } | |
567 | }; | |
568 | ||
569 | template<class Archive> | |
570 | struct load_array_type { | |
571 | template<class T> | |
572 | static void invoke(Archive &ar, T &t){ | |
573 | typedef typename remove_extent< T >::type value_type; | |
574 | ||
575 | // convert integers to correct enum to load | |
576 | // determine number of elements in the array. Consider the | |
577 | // fact that some machines will align elements on boundries | |
578 | // other than characters. | |
579 | std::size_t current_count = sizeof(t) / ( | |
580 | static_cast<char *>(static_cast<void *>(&t[1])) | |
581 | - static_cast<char *>(static_cast<void *>(&t[0])) | |
582 | ); | |
583 | boost::serialization::collection_size_type count; | |
584 | ar >> BOOST_SERIALIZATION_NVP(count); | |
585 | if(static_cast<std::size_t>(count) > current_count) | |
586 | boost::serialization::throw_exception( | |
587 | archive::archive_exception( | |
588 | boost::archive::archive_exception::array_size_too_short | |
589 | ) | |
590 | ); | |
591 | ar >> serialization::make_array(static_cast<value_type*>(&t[0]),count); | |
592 | } | |
593 | }; | |
594 | ||
595 | } // detail | |
596 | ||
597 | template<class Archive, class T> | |
598 | inline void load(Archive & ar, T &t){ | |
599 | // if this assertion trips. It means we're trying to load a | |
600 | // const object with a compiler that doesn't have correct | |
601 | // funtion template ordering. On other compilers, this is | |
602 | // handled below. | |
603 | detail::check_const_loading< T >(); | |
604 | typedef | |
605 | typename mpl::eval_if<is_pointer< T >, | |
606 | mpl::identity<detail::load_pointer_type<Archive> > | |
607 | ,//else | |
608 | typename mpl::eval_if<is_array< T >, | |
609 | mpl::identity<detail::load_array_type<Archive> > | |
610 | ,//else | |
611 | typename mpl::eval_if<is_enum< T >, | |
612 | mpl::identity<detail::load_enum_type<Archive> > | |
613 | ,//else | |
614 | mpl::identity<detail::load_non_pointer_type<Archive> > | |
615 | > | |
616 | > | |
617 | >::type typex; | |
618 | typex::invoke(ar, t); | |
619 | } | |
620 | ||
621 | } // namespace archive | |
622 | } // namespace boost | |
623 | ||
624 | #endif // BOOST_ARCHIVE_DETAIL_ISERIALIZER_HPP |