1/* Copyright 2003-2023 Joaquin M Lopez Munoz.
2 * Distributed under the Boost Software License, Version 1.0.
3 * (See accompanying file LICENSE_1_0.txt or copy at
4 * http://www.boost.org/LICENSE_1_0.txt)
5 *
6 * See http://www.boost.org/libs/multi_index for library home page.
7 */
8
9#ifndef BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP
10#define BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP
11
12#if defined(_MSC_VER)
13#pragma once
14#endif
15
16/* Safe mode machinery, in the spirit of Cay Hortmann's "Safe STL"
17 * (http://www.horstmann.com/safestl.html).
18 * In this mode, containers have to redefine their iterators as
19 * safe_iterator<base_iterator> and keep a tracking object member of
20 * type safe_container<safe_iterator<base_iterator> >. These classes provide
21 * an internal record of which iterators are at a given moment associated
22 * to a given container, and properly mark the iterators as invalid
23 * when the container gets destroyed.
24 * Iterators are chained in a single attached list, whose header is
25 * kept by the container. More elaborate data structures would yield better
26 * performance, but I decided to keep complexity to a minimum since
27 * speed is not an issue here.
28 * Safe mode iterators automatically check that only proper operations
29 * are performed on them: for instance, an invalid iterator cannot be
30 * dereferenced. Additionally, a set of utilty macros and functions are
31 * provided that serve to implement preconditions and cooperate with
32 * the framework within the container.
33 * Iterators can also be unchecked, i.e. they do not have info about
34 * which container they belong in. This situation arises when the iterator
35 * is restored from a serialization archive: only information on the node
36 * is available, and it is not possible to determine to which container
37 * the iterator is associated to. The only sensible policy is to assume
38 * unchecked iterators are valid, though this can certainly generate false
39 * positive safe mode checks.
40 * This is not a full-fledged safe mode framework, and is only intended
41 * for use within the limits of Boost.MultiIndex.
42 */
43
44/* Assertion macros. These resolve to no-ops if
45 * !defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE).
46 */
47
48#if !defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
49#undef BOOST_MULTI_INDEX_SAFE_MODE_ASSERT
50#define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) ((void)0)
51#else
52#if !defined(BOOST_MULTI_INDEX_SAFE_MODE_ASSERT)
53#include <boost/assert.hpp>
54#define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) BOOST_ASSERT(expr)
55#endif
56#endif
57
58#define BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it) \
59 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
60 safe_mode::check_valid_iterator(it), \
61 safe_mode::invalid_iterator);
62
63#define BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(it) \
64 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
65 safe_mode::check_dereferenceable_iterator(it), \
66 safe_mode::not_dereferenceable_iterator);
67
68#define BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(it) \
69 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
70 safe_mode::check_incrementable_iterator(it), \
71 safe_mode::not_incrementable_iterator);
72
73#define BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(it) \
74 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
75 safe_mode::check_decrementable_iterator(it), \
76 safe_mode::not_decrementable_iterator);
77
78#define BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,cont) \
79 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
80 safe_mode::check_is_owner(it,cont), \
81 safe_mode::not_owner);
82
83#define BOOST_MULTI_INDEX_CHECK_BELONGS_IN_SOME_INDEX(it,cont) \
84 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
85 safe_mode::check_belongs_in_some_index(it,cont), \
86 safe_mode::not_owner);
87
88#define BOOST_MULTI_INDEX_CHECK_SAME_OWNER(it0,it1) \
89 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
90 safe_mode::check_same_owner(it0,it1), \
91 safe_mode::not_same_owner);
92
93#define BOOST_MULTI_INDEX_CHECK_VALID_RANGE(it0,it1) \
94 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
95 safe_mode::check_valid_range(it0,it1), \
96 safe_mode::invalid_range);
97
98#define BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(it,it0,it1) \
99 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
100 safe_mode::check_outside_range(it,it0,it1), \
101 safe_mode::inside_range);
102
103#define BOOST_MULTI_INDEX_CHECK_IN_BOUNDS(it,n) \
104 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
105 safe_mode::check_in_bounds(it,n), \
106 safe_mode::out_of_bounds);
107
108#define BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(cont0,cont1) \
109 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
110 safe_mode::check_different_container(cont0,cont1), \
111 safe_mode::same_container);
112
113#define BOOST_MULTI_INDEX_CHECK_EQUAL_ALLOCATORS(cont0,cont1) \
114 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \
115 safe_mode::check_equal_allocators(cont0,cont1), \
116 safe_mode::unequal_allocators);
117
118#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)
119#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
120#include <algorithm>
121#include <boost/core/addressof.hpp>
122 #include <boost/core/noncopyable.hpp>
123#include <boost/multi_index/detail/access_specifier.hpp>
124#include <boost/multi_index/detail/any_container_view.hpp>
125#include <boost/multi_index/detail/iter_adaptor.hpp>
126#include <boost/multi_index/safe_mode_errors.hpp>
127#include <boost/type_traits/is_same.hpp>
128
129#if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
130#include <boost/core/serialization.hpp>
131#endif
132
133#if defined(BOOST_HAS_THREADS)
134#include <boost/detail/lightweight_mutex.hpp>
135#include <boost/multi_index/detail/scoped_bilock.hpp>
136#endif
137
138namespace boost{
139
140namespace multi_index{
141
142namespace safe_mode{
143
144/* Checking routines. Assume the best for unchecked iterators
145 * (i.e. they pass the checking when there is not enough info
146 * to know.)
147 */
148
149template<typename Iterator>
150inline bool check_valid_iterator(const Iterator& it)
151{
152 return it.valid()||it.unchecked();
153}
154
155template<typename Iterator>
156inline bool check_dereferenceable_iterator(const Iterator& it)
157{
158 return (it.valid()&&it!=it.owner()->end())||it.unchecked();
159}
160
161template<typename Iterator>
162inline bool check_incrementable_iterator(const Iterator& it)
163{
164 return (it.valid()&&it!=it.owner()->end())||it.unchecked();
165}
166
167template<typename Iterator>
168inline bool check_decrementable_iterator(const Iterator& it)
169{
170 return (it.valid()&&it!=it.owner()->begin())||it.unchecked();
171}
172
173template<typename Iterator,typename Container>
174inline bool check_is_owner(
175 const Iterator& it,const Container& cont)
176{
177 return (it.valid()&&
178 it.owner()->container()==cont.end().owner()->container())
179 ||it.unchecked();
180}
181
182template<typename Iterator,typename MultiIndexContainer>
183inline bool check_belongs_in_some_index(
184 const Iterator& it,const MultiIndexContainer& cont)
185{
186 return (it.valid()&&it.owner()->end().get_node()==cont.end().get_node())
187 ||it.unchecked();
188}
189
190template<typename Iterator>
191inline bool check_same_owner(const Iterator& it0,const Iterator& it1)
192{
193 return (it0.valid()&&it1.valid()&&
194 it0.owner()->container()==it1.owner()->container())
195 ||it0.unchecked()||it1.unchecked();
196}
197
198template<typename Iterator>
199inline bool check_valid_range(const Iterator& it0,const Iterator& it1)
200{
201 if(!check_same_owner(it0,it1))return false;
202
203 if(it0.valid()){
204 Iterator last=it0.owner()->end();
205 if(it1==last)return true;
206
207 for(Iterator first=it0;first!=last;++first){
208 if(first==it1)return true;
209 }
210 return false;
211 }
212 return true;
213}
214
215template<typename Iterator>
216inline bool check_outside_range(
217 const Iterator& it,const Iterator& it0,const Iterator& it1)
218{
219 if(!check_same_owner(it0,it1))return false;
220
221 if(it0.valid()){
222 Iterator last=it0.owner()->end();
223 bool found=false;
224
225 Iterator first=it0;
226 for(;first!=last;++first){
227 if(first==it1)break;
228
229 /* crucial that this check goes after previous break */
230
231 if(first==it)found=true;
232 }
233 if(first!=it1)return false;
234 return !found;
235 }
236 return true;
237}
238
239template<typename Iterator1,typename Iterator2>
240inline bool check_outside_range(
241 const Iterator1& it,const Iterator2& it0,const Iterator2& it1)
242{
243 if(it.valid()&&it!=it.owner()->end()&&it0.valid()){
244 Iterator2 last=it0.owner()->end();
245 bool found=false;
246
247 Iterator2 first=it0;
248 for(;first!=last;++first){
249 if(first==it1)break;
250
251 /* crucial that this check goes after previous break */
252
253 if(boost::addressof(*first)==boost::addressof(*it))found=true;
254 }
255 if(first!=it1)return false;
256 return !found;
257 }
258 return true;
259}
260
261template<typename Iterator,typename Difference>
262inline bool check_in_bounds(const Iterator& it,Difference n)
263{
264 if(it.unchecked())return true;
265 if(!it.valid()) return false;
266 if(n>0) return it.owner()->end()-it>=n;
267 else return it.owner()->begin()-it<=n;
268}
269
270template<typename Container>
271inline bool check_different_container(
272 const Container& cont0,const Container& cont1)
273{
274 return &cont0!=&cont1;
275}
276
277template<typename Container1,typename Container2>
278inline bool check_different_container(const Container1&,const Container2&)
279{
280 return true;
281}
282
283template<typename Container0,typename Container1>
284inline bool check_equal_allocators(
285 const Container0& cont0,const Container1& cont1)
286{
287 return cont0.get_allocator()==cont1.get_allocator();
288}
289
290/* fwd decls */
291
292template<typename Container> class safe_container;
293template<typename Iterator> void detach_equivalent_iterators(Iterator&);
294
295namespace safe_mode_detail{
296
297/* fwd decls */
298
299class safe_container_base;
300template<typename Dst,typename Iterator>
301void transfer_equivalent_iterators(Dst&,Iterator,boost::true_type);
302template<typename Dst,typename Iterator>
303inline void transfer_equivalent_iterators(Dst&,Iterator&,boost::false_type);
304
305class safe_iterator_base
306{
307public:
308 bool valid()const{return cont!=0;}
309 bool unchecked()const{return unchecked_;}
310
311 inline void detach();
312
313 void uncheck()
314 {
315 detach();
316 unchecked_=true;
317 }
318
319protected:
320 safe_iterator_base():cont(0),next(0),unchecked_(false){}
321
322 explicit safe_iterator_base(safe_container_base* cont_):
323 unchecked_(false)
324 {
325 attach(cont_);
326 }
327
328 safe_iterator_base(const safe_iterator_base& it):
329 unchecked_(it.unchecked_)
330 {
331 attach(it.cont);
332 }
333
334 safe_iterator_base& operator=(const safe_iterator_base& it)
335 {
336 unchecked_=it.unchecked_;
337 safe_container_base* new_cont=it.cont;
338 if(cont!=new_cont){
339 detach();
340 attach(new_cont);
341 }
342 return *this;
343 }
344
345 ~safe_iterator_base()
346 {
347 detach();
348 }
349
350 const safe_container_base* owner()const{return cont;}
351
352BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS:
353
354#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
355 friend class safe_container_base;
356 template<typename>
357 friend class safe_mode::safe_container;
358 template<typename Iterator>
359 friend void safe_mode::detach_equivalent_iterators(Iterator&);
360 template<typename Dst,typename Iterator>
361 friend void safe_mode_detail::transfer_equivalent_iterators(
362 Dst&,Iterator,boost::true_type);
363#endif
364
365 inline void attach(safe_container_base* cont_);
366
367 safe_container_base* cont;
368 safe_iterator_base* next;
369 bool unchecked_;
370};
371
372class safe_container_base:private noncopyable
373{
374public:
375 safe_container_base(){}
376
377BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS:
378
379#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
380 friend class safe_iterator_base;
381 template<typename Iterator>
382 friend void safe_mode::detach_equivalent_iterators(Iterator&);
383 template<typename Dst,typename Iterator>
384 friend void safe_mode_detail::transfer_equivalent_iterators(
385 Dst&,Iterator,boost::true_type);
386#endif
387
388 ~safe_container_base()
389 {
390 /* Detaches all remaining iterators, which by now will
391 * be those pointing to the end of the container.
392 */
393
394 for(safe_iterator_base* it=header.next;it;it=it->next)it->cont=0;
395 header.next=0;
396 }
397
398 void swap(safe_container_base& x)
399 {
400 for(safe_iterator_base* it0=header.next;it0;it0=it0->next)it0->cont=&x;
401 for(safe_iterator_base* it1=x.header.next;it1;it1=it1->next)it1->cont=this;
402 std::swap(header.cont,x.header.cont);
403 std::swap(header.next,x.header.next);
404 }
405
406 safe_iterator_base header;
407
408#if defined(BOOST_HAS_THREADS)
409 boost::detail::lightweight_mutex mutex;
410#endif
411};
412
413void safe_iterator_base::attach(safe_container_base* cont_)
414{
415 cont=cont_;
416 if(cont){
417#if defined(BOOST_HAS_THREADS)
418 boost::detail::lightweight_mutex::scoped_lock lock(cont->mutex);
419#endif
420
421 next=cont->header.next;
422 cont->header.next=this;
423 }
424}
425
426void safe_iterator_base::detach()
427{
428 if(cont){
429#if defined(BOOST_HAS_THREADS)
430 boost::detail::lightweight_mutex::scoped_lock lock(cont->mutex);
431#endif
432
433 safe_iterator_base *prev_,*next_;
434 for(prev_=&cont->header;(next_=prev_->next)!=this;prev_=next_){}
435 prev_->next=next;
436 cont=0;
437 }
438}
439
440} /* namespace multi_index::safe_mode::safe_mode_detail */
441
442/* In order to enable safe mode on a container:
443 * - The container must keep a member of type safe_container<iterator>,
444 * - iterators must be generated via safe_iterator, which adapts a
445 * preexistent unsafe iterator class. safe_iterators are passed the
446 * address of the previous safe_container member at construction time.
447 */
448
449template<typename Iterator>
450class safe_iterator:
451 public detail::iter_adaptor<safe_iterator<Iterator>,Iterator>,
452 public safe_mode_detail::safe_iterator_base
453{
454 typedef detail::iter_adaptor<safe_iterator,Iterator> super;
455 typedef safe_mode_detail::safe_iterator_base safe_super;
456
457public:
458 typedef typename Iterator::reference reference;
459 typedef typename Iterator::difference_type difference_type;
460
461 safe_iterator(){}
462 explicit safe_iterator(safe_container<safe_iterator>* cont_):
463 safe_super(cont_){}
464 template<typename T0>
465 safe_iterator(const T0& t0,safe_container<safe_iterator>* cont_):
466 super(Iterator(t0)),safe_super(cont_){}
467 template<typename T0,typename T1>
468 safe_iterator(
469 const T0& t0,const T1& t1,safe_container<safe_iterator>* cont_):
470 super(Iterator(t0,t1)),safe_super(cont_){}
471 safe_iterator(const safe_iterator& x):super(x),safe_super(x){}
472
473 safe_iterator& operator=(const safe_iterator& x)
474 {
475 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
476 this->base_reference()=x.base_reference();
477 safe_super::operator=(x);
478 return *this;
479 }
480
481 const safe_container<safe_iterator>* owner()const
482 {
483 return
484 static_cast<const safe_container<safe_iterator>*>(
485 this->safe_super::owner());
486 }
487
488 /* get_node is not to be used by the user */
489
490 typedef typename Iterator::node_type node_type;
491
492 node_type* get_node()const{return this->base_reference().get_node();}
493
494private:
495 friend class boost::multi_index::detail::iter_adaptor_access;
496
497 reference dereference()const
498 {
499 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
500 BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(*this);
501 return *(this->base_reference());
502 }
503
504 bool equal(const safe_iterator& x)const
505 {
506 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
507 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
508 BOOST_MULTI_INDEX_CHECK_SAME_OWNER(*this,x);
509 return this->base_reference()==x.base_reference();
510 }
511
512 void increment()
513 {
514 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
515 BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(*this);
516 ++(this->base_reference());
517 }
518
519 void decrement()
520 {
521 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
522 BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(*this);
523 --(this->base_reference());
524 }
525
526 void advance(difference_type n)
527 {
528 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
529 BOOST_MULTI_INDEX_CHECK_IN_BOUNDS(*this,n);
530 this->base_reference()+=n;
531 }
532
533 difference_type distance_to(const safe_iterator& x)const
534 {
535 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
536 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x);
537 BOOST_MULTI_INDEX_CHECK_SAME_OWNER(*this,x);
538 return x.base_reference()-this->base_reference();
539 }
540
541#if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
542 /* Serialization. Note that Iterator::save and Iterator:load
543 * are assumed to be defined and public: at first sight it seems
544 * like we could have resorted to the public serialization interface
545 * for doing the forwarding to the adapted iterator class:
546 * ar<<base_reference();
547 * ar>>base_reference();
548 * but this would cause incompatibilities if a saving
549 * program is in safe mode and the loading program is not, or
550 * viceversa --in safe mode, the archived iterator data is one layer
551 * deeper, this is especially relevant with XML archives.
552 * It'd be nice if Boost.Serialization provided some forwarding
553 * facility for use by adaptor classes.
554 */
555
556 friend class boost::serialization::access;
557
558 template<class Archive>
559 void serialize(Archive& ar,const unsigned int version)
560 {
561 core::split_member(ar,*this,version);
562 }
563
564 template<class Archive>
565 void save(Archive& ar,const unsigned int version)const
566 {
567 BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this);
568 this->base_reference().save(ar,version);
569 }
570
571 template<class Archive>
572 void load(Archive& ar,const unsigned int version)
573 {
574 this->base_reference().load(ar,version);
575 safe_super::uncheck();
576 }
577#endif
578};
579
580template<typename Iterator>
581class safe_container:public safe_mode_detail::safe_container_base
582{
583 typedef safe_mode_detail::safe_container_base super;
584
585 detail::any_container_view<Iterator> view;
586
587public:
588 template<typename Container>
589 safe_container(const Container& c):view(c){}
590
591 const void* container()const{return view.container();}
592 Iterator begin()const{return view.begin();}
593 Iterator end()const{return view.end();}
594
595 void detach_dereferenceable_iterators()
596 {
597 Iterator end_=view.end();
598 Iterator *prev_,*next_;
599 for(
600 prev_=static_cast<Iterator*>(&this->header);
601 (next_=static_cast<Iterator*>(prev_->next))!=0;){
602 if(*next_!=end_){
603 prev_->next=next_->next;
604 next_->cont=0;
605 }
606 else prev_=next_;
607 }
608 }
609
610 void swap(safe_container<Iterator>& x)
611 {
612 super::swap(x);
613 }
614};
615
616/* Invalidates all iterators equivalent to that given. Safe containers
617 * must call this when deleting elements: the safe mode framework cannot
618 * perform this operation automatically without outside help.
619 */
620
621template<typename Iterator>
622inline void detach_equivalent_iterators(Iterator& it)
623{
624 if(it.valid()){
625 {
626#if defined(BOOST_HAS_THREADS)
627 boost::detail::lightweight_mutex::scoped_lock lock(it.cont->mutex);
628#endif
629
630 Iterator *prev_,*next_;
631 for(
632 prev_=static_cast<Iterator*>(&it.cont->header);
633 (next_=static_cast<Iterator*>(prev_->next))!=0;){
634 if(next_!=&it&&*next_==it){
635 prev_->next=next_->next;
636 next_->cont=0;
637 }
638 else prev_=next_;
639 }
640 }
641 it.detach();
642 }
643}
644
645/* Transfers iterators equivalent to that given to Dst, if that container has
646 * the same iterator type; otherwise, detaches them.
647 */
648
649template<typename Dst,typename Iterator>
650inline void transfer_equivalent_iterators(Dst& dst,Iterator& i)
651{
652 safe_mode_detail::transfer_equivalent_iterators(
653 dst,i,boost::is_same<Iterator,typename Dst::iterator>());
654}
655
656namespace safe_mode_detail{
657
658template<typename Dst,typename Iterator>
659inline void transfer_equivalent_iterators(
660 Dst& dst,Iterator it,boost::true_type /* same iterator type */)
661{
662 if(it.valid()){
663 {
664 safe_container_base* cont_=dst.end().cont;
665
666#if defined(BOOST_HAS_THREADS)
667 detail::scoped_bilock<boost::detail::lightweight_mutex>
668 scoped_bilock(it.cont->mutex,cont_->mutex);
669#endif
670
671 Iterator *prev_,*next_;
672 for(
673 prev_=static_cast<Iterator*>(&it.cont->header);
674 (next_=static_cast<Iterator*>(prev_->next))!=0;){
675 if(next_!=&it&&*next_==it){
676 prev_->next=next_->next;
677 next_->cont=cont_;
678 next_->next=cont_->header.next;
679 cont_->header.next=next_;
680 }
681 else prev_=next_;
682 }
683 }
684 /* nothing to do with it, was passed by value and will die now */
685 }
686}
687
688template<typename Dst,typename Iterator>
689inline void transfer_equivalent_iterators(
690 Dst&,Iterator& it,boost::false_type /* same iterator type */)
691{
692 detach_equivalent_iterators(it);
693}
694
695} /* namespace multi_index::safe_mode::safe_mode_detail */
696
697} /* namespace multi_index::safe_mode */
698
699} /* namespace multi_index */
700
701#if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION)
702namespace serialization{
703template<typename Iterator>
704struct version<
705 boost::multi_index::safe_mode::safe_iterator<Iterator>
706>
707{
708 BOOST_STATIC_CONSTANT(
709 int,value=boost::serialization::version<Iterator>::value);
710};
711} /* namespace serialization */
712#endif
713
714} /* namespace boost */
715
716#endif /* BOOST_MULTI_INDEX_ENABLE_SAFE_MODE */
717
718#endif
719