1
// Copyright (c) 2003 Raoul M. Gough
3
// Use, modification and distribution is subject to the Boost Software
4
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy
5
// at http://www.boost.org/LICENSE_1_0.txt)
7
// Header file container_proxy.hpp
9
// A container-wrapper that provides Python-style reference semantics
10
// for values stored in vector-like containers via element proxies.
13
// size() == m_proxies.size()
14
// for 0 <= i < size()
15
// m_proxies[i].get() != 0
16
// m_proxies[i]->owner() == this
17
// m_proxies[i]->index() == i
18
// m_proxies[i]->m_element_ptr.get() == 0
22
// 2003/ 8/26 rmg File creation
23
// 2003/10/23 rmg Change pointer container from map to sequence
24
// 2008/12/08 Roman Change indexing suite layout
26
// $Id: container_proxy.hpp,v 1.1.2.28 2004/02/08 18:57:42 raoulgough Exp $
29
#ifndef BOOST_PYTHON_INDEXING_CONTAINER_PROXY_HPP
30
#define BOOST_PYTHON_INDEXING_CONTAINER_PROXY_HPP
32
#include <indexing_suite/proxy_iterator.hpp>
33
#include <indexing_suite/shared_proxy_impl.hpp>
34
#include <indexing_suite/element_proxy.hpp>
35
#include <indexing_suite/element_proxy_traits.hpp>
36
#include <indexing_suite/workaround.hpp>
37
#include <indexing_suite/methods.hpp>
41
#include <boost/shared_ptr.hpp>
42
#include <boost/mpl/apply.hpp>
43
#include <boost/iterator/iterator_traits.hpp>
44
#include <indexing_suite/container_traits.hpp>
45
#include <indexing_suite/container_suite.hpp>
46
#include <indexing_suite/algorithms.hpp>
48
namespace boost { namespace python { namespace indexing {
50
template<typename T> struct identity {
53
static T & get(T & obj) { return obj; }
54
static T const & get(T const & obj) { return obj; }
56
static T create () { return T(); }
57
static T copy (T const ©) { return copy; }
58
static void assign (T &to, T const &from) { to = from; }
59
static void pre_destruction (T &) { }
60
static void swap (T &one, T &two) { std::swap (one, two); }
63
template<typename P> struct deref {
66
typedef typename boost::iterator_value<P>::type value;
68
static value & get (P & ptr) { return *ptr; }
69
static value const & get (P const & ptr) { return *ptr; }
71
static P create () { return P(); }
72
static P copy (P const ©) { return copy; }
73
static void assign (P &to, P const &from) { to = from; }
74
static void pre_destruction (P &) { }
75
static void swap (P &one, P &two) { std::swap (one, two); }
78
struct vector_generator {
79
// Generates vector type for any element type with default allocator
80
template<typename Element> struct apply {
81
typedef std::vector<Element> type;
85
#if BOOST_WORKAROUND (BOOST_MSVC, == 1200)
86
// Early template instantiation (ETI) workaround
88
template<typename Container> struct msvc6_iterator {
89
typedef Container::iterator type;
92
template<> struct msvc6_iterator<int> {
98
template<class Container,
99
class Holder = identity<Container>,
100
class Generator = vector_generator>
101
class container_proxy
103
typedef container_proxy<Container, Holder, Generator> self_type;
104
typedef typename Container::iterator raw_iterator;
105
typedef ::boost::detail::iterator_traits<raw_iterator> raw_iterator_traits;
107
#if !defined (BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
108
template<class C> friend class shared_proxy_impl;
109
template<class C, typename E, typename T, typename S, typename I>
110
friend class proxy_iterator;
114
typedef typename Holder::held_type held_type;
116
typedef typename Container::size_type size_type;
117
typedef typename Container::difference_type difference_type;
119
typedef shared_proxy_impl<self_type> shared_proxy;
121
typedef typename Container::value_type raw_value_type;
123
typedef element_proxy<self_type> value_type;
124
typedef value_type reference; // Already has ref. semantics
126
typedef const_element_proxy<self_type> const_value_type;
127
typedef const_value_type const_reference; // Ref. semantics
129
typedef proxy_iterator <self_type, value_type, raw_iterator_traits,
130
size_type, raw_iterator> iterator;
131
typedef iterator const_iterator; // No const_iterator yet implemented
135
template<typename Iter> container_proxy (Iter start, Iter finish)
136
// Define inline for MSVC6 compatibility
137
: m_held_obj (Holder::create()),
140
insert (begin(), start, finish);
144
explicit container_proxy (held_type const &h);
146
container_proxy (container_proxy const &);
147
container_proxy &operator= (container_proxy const &);
150
Container const &raw_container() const; // OK to expose const reference
152
reference at (size_type index);
153
const_reference at (size_type index) const;
155
reference operator[] (size_type index) { return at(index); }
156
const_reference operator[] (size_type index) const { return at(index); }
158
size_type size () const { return raw_container().size(); }
159
size_type capacity () const { return raw_container().capacity(); }
160
void reserve (size_type s);
163
iterator begin() { return iterator (this, static_cast<size_type>(0)); }
164
iterator end() { return iterator (this, raw_container().size()); }
166
iterator erase (iterator);
167
iterator erase (iterator, iterator);
168
iterator insert (iterator, raw_value_type const &);
170
template<typename Iter> void insert (iterator iter, Iter from, Iter to)
171
// Define here for MSVC6 compatibility
173
// Forward insertion to the right overloaded version
174
typedef typename BOOST_ITERATOR_CATEGORY<Iter>::type category;
175
insert (iter, from, to, category());
178
void push_back (raw_value_type const ©) { insert (end(), copy); }
180
value_type pop_back () {
181
value_type result = at (size() - 1);
187
// These functions are useful only when client code has direct
188
// non-const acccess to the raw container (e.g. via an indirect
189
// holder supplied to our constructor). Any code that directly
190
// modifies the contents of the raw container (by replacing,
191
// inserting or erasing elements) must notify the container_proxy.
193
void detach_proxy (size_type index);
194
void detach_proxies (size_type from, size_type to);
195
// Call before overwriting element(s) in the raw container
197
void prepare_erase (size_type from, size_type to);
198
// Call before erasing elements directly from the raw container
200
void notify_insertion (size_type from, size_type to);
201
// Call after inserting elements directly into the raw container
204
// Convenient replacement of elements (automatic proxy detachment)
205
void replace (size_type index, raw_value_type const &);
206
// template<typename Iter> void replace (size_type index, Iter, Iter);
208
void swap_elements (size_type index1, size_type index2);
210
bool is_valid () const; // Check the class invariant (for testing purposes)
213
// Overloads for insertions with/without useful std::distance
214
template<typename Iter>
215
void insert (iterator iter, Iter from, Iter to, std::forward_iterator_tag)
216
// Define here for MSVC6 compatibility
218
assert (iter.ptr == this);
219
size_type count = std::distance (from, to);
221
// Add empty proxy pointers for the new value(s) (could throw)
222
m_proxies.insert (m_proxies.begin() + iter.index, count, pointer_impl());
226
// Insert the new element(s) into the real container (could throw)
227
raw_container().insert(
228
raw_container().begin() + iter.index,
234
// Create new proxies for the new elements (could throw)
235
write_proxies (iter.index, iter.index + count);
240
raw_container().erase(
241
raw_container().begin() + iter.index,
242
raw_container().begin() + iter.index + count);
251
m_proxies.begin() + iter.index,
252
m_proxies.begin() + iter.index + count);
257
// Adjust any proxies after the inserted elements (nothrow)
259
m_proxies.begin() + iter.index + count,
261
static_cast<difference_type> (count));
264
template<typename Iter>
265
void insert (iterator iter, Iter from, Iter to, std::input_iterator_tag)
266
// Define here for MSVC6 compatibility
268
// insert overload for iterators where we *can't* get distance()
269
// so just insert elements one at a time
272
iter = insert (iter, *from++) + 1;
277
typedef boost::shared_ptr<shared_proxy> pointer_impl;
279
typedef typename mpl::apply1<Generator, pointer_impl>::type
282
#if BOOST_WORKAROUND (BOOST_MSVC, == 1200)
283
typedef detail::msvc6_iterator<pointer_container>::type pointer_iterator;
285
typedef typename pointer_container::iterator pointer_iterator;
288
#if defined (BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
289
// Proxies need mutable access, and can't be friends with MSVC6
292
Container &raw_container();
295
void adjust_proxies (pointer_iterator, pointer_iterator, difference_type);
296
void write_proxies (size_type, size_type);
297
bool clear_proxy (pointer_impl &); // detach and do not reset
298
void clear_proxies (size_type, size_type); // detach and do not reset
299
void claim_all_proxies (); // Makes all proxies point at this object
302
held_type m_held_obj;
303
pointer_container m_proxies;
306
template<class Container, class Holder, class Generator>
307
container_proxy<Container, Holder, Generator>
309
: m_held_obj (Holder::create()),
312
// Container is empty - no further processing
315
template<class Container, class Holder, class Generator>
316
container_proxy<Container, Holder, Generator>
317
::container_proxy (held_type const &held)
318
: m_held_obj (Holder::copy (held)),
321
write_proxies (0, size());
324
template<class Container, class Holder, class Generator>
325
container_proxy<Container, Holder, Generator>
326
::container_proxy (container_proxy const ©)
327
: m_held_obj (Holder::copy (copy.m_held_obj)),
330
write_proxies (0, size()); // Create our own proxies for the copied values
333
template<class Container, class Holder, class Generator>
334
container_proxy<Container, Holder, Generator> &
335
container_proxy<Container, Holder, Generator>
336
::operator= (container_proxy const ©)
338
container_proxy<Container, Holder, Generator> temp (copy);
339
// This could throw, but none of the remaining operations can
341
Holder::swap (m_held_obj, temp.m_held_obj);
342
std::swap (m_proxies, temp.m_proxies);
344
claim_all_proxies ();
345
temp.claim_all_proxies (); // Prepare for detach
348
// temp destruction detaches any proxies that used to belong to us
351
template<class Container, class Holder, class Generator>
352
container_proxy<Container, Holder, Generator>
353
::~container_proxy ()
355
// Copy original values into any proxies being shared by external pointers
356
clear_proxies (0, size());
357
Holder::pre_destruction (m_held_obj);
360
template<class Container, class Holder, class Generator>
362
container_proxy<Container, Holder, Generator>
365
return Holder::get (m_held_obj);
368
template<class Container, class Holder, class Generator>
370
container_proxy<Container, Holder, Generator>
371
::raw_container () const
373
return Holder::get (m_held_obj);
376
template<class Container, class Holder, class Generator>
377
void container_proxy<Container, Holder, Generator>::reserve (size_type size)
379
raw_container().reserve (size);
380
m_proxies.reserve (size);
383
template<class Container, class Holder, class Generator>
384
BOOST_DEDUCED_TYPENAME container_proxy<Container, Holder, Generator>::reference
385
container_proxy<Container, Holder, Generator>
386
::at (size_type index)
388
pointer_impl const &ptr = m_proxies.BOOST_PYTHON_INDEXING_AT (index);
389
assert (ptr->owner() == this);
390
assert (ptr->index() == index);
391
return reference (ptr);
394
template<class Container, class Holder, class Generator>
395
BOOST_DEDUCED_TYPENAME container_proxy<Container, Holder, Generator>::const_reference
396
container_proxy<Container, Holder, Generator>
397
::at (size_type index) const
399
pointer_impl const &ptr = m_proxies.BOOST_PYTHON_INDEXING_AT (index);
400
assert (ptr->owner() == this);
401
assert (ptr->index() == index);
402
return const_reference (ptr);
405
template<class Container, class Holder, class Generator>
407
container_proxy<Container, Holder, Generator>
408
::replace (size_type index, raw_value_type const ©)
410
detach_proxy (index);
411
raw_container().BOOST_PYTHON_INDEXING_AT (index) = copy;
412
write_proxies (index, index + 1);
415
template<class Container, class Holder, class Generator>
417
container_proxy<Container, Holder, Generator>
418
::swap_elements (size_type index1, size_type index2)
420
pointer_impl &ptr1 = m_proxies[index1];
421
pointer_impl &ptr2 = m_proxies[index2];
423
assert (ptr1->owner() == this);
424
assert (ptr2->owner() == this);
425
assert (ptr1->index() == index1);
426
assert (ptr2->index() == index2);
428
// Swap produces the diagrammed transformation. Any external
429
// pointers that refer to proxy1 or proxy2 will end up still
430
// pointing to their original (now relocated) values.
432
// .. ptr1 .. ptr2 .. .. ptr1 .. ptr2 .. (m_proxies)
437
// proxy1 proxy2 --> proxy1 proxy2
442
// .. v1 ... v2 .. .. v2 .. v1 .. (raw_container)
444
std::swap (ptr1->m_index, ptr2->m_index);
445
std::swap (ptr1, ptr2);
446
std::swap (raw_container()[index1], raw_container()[index2]);
448
assert (m_proxies[index1]->index() == index1);
449
assert (m_proxies[index2]->index() == index2);
452
template<class Container, class Holder, class Generator>
453
BOOST_DEDUCED_TYPENAME container_proxy<Container, Holder, Generator>::iterator
454
container_proxy<Container, Holder, Generator>::erase (iterator iter)
456
return erase (iter, iter + 1);
459
template<class Container, class Holder, class Generator>
460
BOOST_DEDUCED_TYPENAME container_proxy<Container, Holder, Generator>::iterator
461
container_proxy<Container, Holder, Generator>::erase(
462
iterator from, iterator to)
464
assert (from.ptr == this);
465
assert (to.ptr == this);
467
// Detach and remove the proxies for the about-to-be-erased elements
468
prepare_erase (from.index, to.index);
470
// Erase the elements from the real container
472
= raw_container().erase(
473
raw_container().begin() + from.index,
474
raw_container().begin() + to.index);
476
return iterator (this, result);
479
template<class Container, class Holder, class Generator>
480
BOOST_DEDUCED_TYPENAME container_proxy<Container, Holder, Generator>::iterator
481
container_proxy<Container, Holder, Generator>::insert(
482
iterator iter, raw_value_type const ©)
484
// Use the iterator-based version by treating the value as an
485
// array of size one (see section 5.7/4 of the C++98 standard)
486
insert (iter, ©, (©) + 1, std::random_access_iterator_tag());
491
template<class Container, class Holder, class Generator>
492
bool container_proxy<Container, Holder, Generator>::clear_proxy(
495
// Warning - this can break the class invariant. Use only when the
496
// pointer is about to be overwritten or removed from m_proxies
498
assert (ptr->owner() == this);
502
ptr->detach (); // Cause proxy to copy element value
508
// If the pointer isn't shared, don't bother causing a copy of
509
// the container element, since the proxy is about to be
510
// deleted or reused.
515
template<class Container, class Holder, class Generator>
516
void container_proxy<Container, Holder, Generator>::clear_proxies(
517
size_type from_index, size_type to_index)
519
while (from_index != to_index)
521
clear_proxy (m_proxies[from_index]);
526
template<class Container, class Holder, class Generator>
527
void container_proxy<Container, Holder, Generator>
528
::detach_proxy (size_type index)
530
pointer_impl &ptr = m_proxies[index];
532
assert (ptr->index() == index);
534
if (clear_proxy (ptr))
536
// To maintain class invariant
537
ptr.reset (new shared_proxy (this, index));
541
template<class Container, class Holder, class Generator>
542
void container_proxy<Container, Holder, Generator>::detach_proxies(
543
size_type from_index, size_type to_index)
545
while (from_index != to_index)
547
detach_proxy (from_index);
552
template<class Container, class Holder, class Generator>
553
void container_proxy<Container, Holder, Generator>
554
::prepare_erase (size_type from_index, size_type to_index)
556
difference_type deleting = to_index - from_index;
557
pointer_iterator erase_begin = m_proxies.begin() + from_index;
558
pointer_iterator erase_end = m_proxies.begin() + to_index;
560
// Adjust the indexes of any trailing proxies
561
adjust_proxies (erase_end, m_proxies.end(), -deleting);
563
// Detach any proxies without updating our pointers to them
564
clear_proxies (from_index, to_index);
566
// Remove the pointers
567
m_proxies.erase (erase_begin, erase_end);
570
template<class Container, class Holder, class Generator>
571
void container_proxy<Container, Holder, Generator>::notify_insertion(
572
size_type from_index, size_type to_index)
574
size_type count = to_index - from_index;
577
m_proxies.begin() + from_index, count, pointer_impl());
581
write_proxies (from_index, to_index); // Could throw
587
m_proxies.begin() + from_index,
588
m_proxies.begin() + to_index);
593
// Adjust any proxies after the inserted elements (nothrow)
595
m_proxies.begin() + to_index,
597
static_cast<difference_type> (count));
600
template<class Container, class Holder, class Generator>
601
void container_proxy<Container, Holder, Generator>::adjust_proxies(
602
pointer_iterator from,
604
difference_type offset)
608
(*from)->m_index += offset;
613
template<class Container, class Holder, class Generator>
614
void container_proxy<Container, Holder, Generator>::write_proxies(
615
size_type from, size_type to)
617
// (over)write proxy pointers in the given range. Re-uses existing
618
// shared_proxy objects where possible. Does not call detach_proxy
619
// since it is assumed that the original values could have already
620
// been modified and copying them now would be wrong.
624
pointer_impl &ptr = m_proxies[from];
626
if ((ptr.get() == 0) || (!ptr.unique()))
628
// Either no proxy yet allocated here, or there is one
629
// but it is being shared by an external pointer.
630
ptr.reset (new shared_proxy (this, from));
635
// Re-use the existing object since we have the only pointer to it
636
assert (ptr->owner() == this);
644
template<class Container, class Holder, class Generator>
645
void container_proxy<Container, Holder, Generator>::claim_all_proxies ()
647
for (pointer_iterator iter = m_proxies.begin();
648
iter != m_proxies.end();
651
(*iter)->m_owner_ptr = this;
655
template<class Container, class Holder, class Generator>
656
bool container_proxy<Container, Holder, Generator>::is_valid () const
658
bool ok = size() == m_proxies.size(); // Sizes must match
660
for (size_type count = 0; ok && (count < size()); ++count)
662
pointer_impl const &ptr = m_proxies[count];
664
ok = ptr.get() && (ptr->owner() == this) && (ptr->index() == count)
665
&& !ptr->m_element_ptr.get();
671
/////////////////////////////////////////////////////////////////////////
672
// ContainerTraits implementation for container_proxy instances
673
/////////////////////////////////////////////////////////////////////////
675
template<typename Container>
676
struct container_proxy_traits : random_access_sequence_traits<Container>
678
typedef Container container;
679
typedef typename container::raw_value_type value_type; // insert, ...
680
typedef typename container::raw_value_type key_type; // find, count, ...
681
typedef typename container::reference reference; // return values
683
typedef typename BOOST_PYTHON_INDEXING_CALL_TRAITS <value_type>::param_type
685
typedef typename BOOST_PYTHON_INDEXING_CALL_TRAITS <key_type>::param_type
688
#if defined (BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
689
// value_traits for the reference type (i.e. our element_proxy
690
// instance) supplies a custom visit_container_class. Compilers
691
// without partial specialization need help here.
693
typedef element_proxy_traits<Container> value_traits_type;
695
// Hide base class visit_container_class, which would call the
696
// unspecialized value_traits version
697
template<typename PythonClass, typename Policy>
698
static void visit_container_class(
699
PythonClass &pyClass, Policy const &policy)
701
value_traits_type::visit_container_class (pyClass, policy);
706
#if !defined (BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
708
///////////////////////////////////////////////////////////////////////
709
// algorithms support for container_proxy instances
710
///////////////////////////////////////////////////////////////////////
712
template <typename RawContainer, typename Holder, typename Generator>
713
class algorithms_selector<container_proxy<RawContainer, Holder, Generator> >
715
typedef container_proxy<RawContainer, Holder, Generator> Container;
717
typedef container_proxy_traits<Container> mutable_traits;
718
typedef container_proxy_traits<Container const> const_traits;
721
typedef default_algorithms<mutable_traits> mutable_algorithms;
722
typedef default_algorithms<const_traits> const_algorithms;
728
method_set_type MethodMask = all_methods,
729
class Traits = container_proxy_traits<Container>
731
struct container_proxy_suite
732
: container_suite<Container, MethodMask, default_algorithms<Traits> >
738
#endif // BOOST_PYTHON_INDEXING_CONTAINER_PROXY_HPP