Proxy --- an indirect container adaptor

Is the following design valid, and if so, would it make a useful addition to the Boost library? I enclose a draft implementation, and look forward to any comments. /Christian - - - It can quite often be of interest to convert a program that uses a direct container into using an indirect container of smart pointers to the objects instead, either just temporarily to see what happens to performance, or permanently if it turns out to be beneficial. Unfortunately, this normally means that the program has to be modified in many different places, even for programs that are not affected as such by the slightly different semantics that indirect containers have. The proxy indirect container header file makes it possible to switch between direct and indirect containers by changing a single typedef in any such program. For example, the following test program was modified to use an indirect vector instead of a direct one by just changing the type declaration from std::vector<person> to proxy_vector<person>::self. //=================================================================== #include <boost/operators.hpp> #include "proxy.hpp" int calls_to_copy_constructor = 0; //=================================================================== class person : boost::totally_ordered<person> //------------------------------------------------------------------- { public: string name; int age; person() {} person(const string& in_name, int in_age); person(const person& other) : name(other.name), age(other.age) { ++calls_to_copy_constructor; } // Compares first name, then age friend bool operator== (const person& a, const person& b); friend bool operator< (const person& a, const person& b); bool read(istream& in_file); }; ostream& operator<< (ostream& out_file, const person& x); //=================================================================== int main (int argc, char* argv[]) //------------------------------------------------------------------- { person a_person; person someone_special("Alice", 27); ifstream in_file("test_proxy.in"); // typedef std::vector<person> buf_type; // <---- typedef proxy_vector<person>::self buf_type; // <---- buf_type buf(5, person("Undefined", -1)); buf.front() = person("First", 1); *(buf.begin() + 1) = person("Second", 2); buf[2] = person("Third", 3); (buf.begin() + 3)->name = "Incomplete"; while (a_person.read(in_file)) { buf.push_back(a_person); } std::sort(buf.begin(), buf.end()); for (buf_type::iterator it = buf.begin(); it != buf.end(); ++it) { cout << *it; if (it->age < 0) cout << " Not a proper age"; if (*it == someone_special) cout << " Someone special"; if (it != buf.begin() && (*it == *(it – 1))) cout << " Duplicate record"; cout << endl; } buf_type::iterator end_it = std::unique(buf.begin(), buf.end()); cout << "\nNumber of unique records: " << (end_it - buf.begin()); cout << "\n\nCalls to 'person's copy constructor: " << calls_to_copy_constructor << endl; return 0; } //=================================================================== So how does this work? First of all, the declaration proxy_vector<T>::self is short for proxy_container< std::vector< proxy<T> > > There are convenience templates like this for the basic container types in STL, but by using the general form the proxy container adaptor can be used on any container that has an STL compliant interface. Class proxy At the very center of the design is the proxy template class. A proxy<T> object is a smart pointer that has a boost::shared_ptr<T> as its only data member. It constructs, assigns, and takes responsibility for deleting the object in the same way as a shared_ptr. It does not support any pointer arithmetic, however, so the ++ and -- operators are undefined. Without pointer arithmetic, there is very little use for the comparison operators as they are defined for shared_ptr, so these are instead given the semantics of element_compare, which means that they compare the two objects as such rather than the pointers. (There is a conversion function to shared_ptr that can be used to test the pointers as such if necessary.) With these definitions, we can use the STL algorithms directly on any Container< proxy<T> >, and they will produce the same results as they would for a Container<T>. This applies both to algorithms like std::sort, which depends on the < operator for comparisons, and algorithms like unique, which relies on the == operator. The proxy class also contains an implicit conversion operator from proxy<T> to T&. This means that we can use a proxy<T> as the actual parameter to any function that expects a T for input or output, or, to put it another way, that the proxy is automatically “cashed in” for a T object as soon as this is required. In particular, this means that we can copy T objects from a Container< proxy<T> > into T variables, in exactly the same way as if it had been a Container<T>, since the proxy<T> object that the iterator delivers will be automatically converted to a T if it is assigned to a T variable. If we assign it to a proxy<T> variable, it will of course remain a proxy. To handle assignments to proxy variables, the default assignment operator is supplemented by operator=(const T&). This operator will create a proxy that points at a new copy of the T object, and assign it to the left hand variable. This means that we now have a Container< proxy<T> > that behaves like a Container<T> for comparisons, and when we retrieve whole objects from the container. Class proxy_iterator To get operator-> working in the same way as for a Container<T>, we define the classes proxy_iterator and const_proxy_iterator. A proxy_iterator is publicly derived from the iterator of the underlying Container< proxy<T> >. It works just like standard iterator in all respects except one, which is that operator-> does a double dereferencing, so that it will deliver a pointer to the T object itself. This lets us use the -> operator in the same way that we would with a direct container. operator* is not changed, however, and retains its single dereferencing semantics. Are we really allowed to define these operators like this? --Yes, we are. While it is true that this means that the semantics will be different for (*x).y and x->y, this does not affect the STL algorithms in any way, since they don’t make any use of the -> operator. (How could they, when they’re supposed to work on the built-in types as well?) The proxy iterators can be mixed freely with the underlying standard iterators, since the derived-to-base-class conversion will convert in one direction, and the proxy iterators have have a conversion constructor that converts in the other direction. Since the compiler will prefer a derived-to-base conversion over a user-defined conversion, the ambiguity problems that are normally associated with having two types that can be implicitly converted in both directions are eliminated. Class proxy_container The class proxy_container, finally, inherits from the underlying Container< proxy<T> > and overrides the definitions for iterator and const_iterator so that the proxy iterators are used instead. It also redefines the standard container member functions that return an iterator so that they return the appropriate proxy iterator. In order to be able to insert T objects directly into the proxy container, the class proxy_container also redefines all members that insert an object into the container so that there are two versions of each function: one that takes a proxy<T> as input and inserts a new proxy that points at the same object, and one that takes a T object and inserts a proxy that points at a newly created copy of the object. Taken together, these classes let us emulate the interface of a Container<T> with something that is actually an indirect container. Since it is only a single type declaration that has to be changed to switch between direct and indirect containers, it becomes practical to do this for testing purposes. The indirect containers still retain the full interface of a traditional container of smart pointers, so the additional capabilities of an indirect container are available in the usual way. - - - I have uploaded the full source code for the draft implementation, as well as and the test program shown here, to the files section at Yahoo, and I have tested it with MSVC6 and gcc 3.3 on Windows XP. For MSVC6 there is the problem that the implementation of std::vector::iterator is just a plain pointer and not a standards compliant iterator, so proxy_vector uses a deque rather than a vector as a workaround. File proxy.hpp: (slightly abridged) //================================================================== // 20-feb-2004 Christian Engström (christian.engstrom@glindra.org) //------------------------------------------------------------------ #include <vector> #include <deque> #include <list> #include <boost/smart_ptr.hpp> //================================================================== // 'proxy' //------------------------------------------------------------------ template <class T> class proxy { private: typedef proxy<T> self; boost::shared_ptr<T> m_sp; public: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Types typedef T element_type; // "Inherited" from 'boost::shared_ptr' typedef T value_type; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors proxy() {} template<class Y> proxy(const proxy<Y>& other) : m_sp(other.as_shared_ptr()) {} template<class Y> explicit proxy(const boost::shared_ptr<Y>& other) : m_sp(other) {} template<class Y> explicit proxy(const boost::weak_ptr<Y>& other) : m_sp(other) {} template<class Y> explicit proxy(std::auto_ptr<Y>& other) : m_sp(other) {} template<class Y> explicit proxy(Y* other) : m_sp(other) {} // Assignment self& operator=(const self& other) {m_sp = other.m_sp; return *this;} self& operator=(const T& other) {*this = proxy<T>(new T(other)); return *this;} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Conversions operator T&() const {return **this;} // Implicit conversion to T bool is_null() const {return !m_sp;} boost::shared_ptr<T>& as_shared_ptr() {return m_sp;} const boost::shared_ptr<T>& as_shared_ptr() const {return m_sp;} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Comparisons are made on the value, never the pointer. bool operator== (const self& other) const {return **this == *other;} bool operator!= (const self& other) const {return **this != *other;} bool operator< (const self& other) const {return **this < *other;} bool operator> (const self& other) const {return **this > *other;} bool operator<= (const self& other) const {return **this <= *other;} bool operator>= (const self& other) const {return **this >= *other;} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Dereferencing T& operator*() const {return *m_sp;} T* operator->() const {return m_sp.get();} T* get() const {return m_sp.get();} T* as_ptr() const {return m_sp.get();} }; //================================================================== // 'proxy_iterator' //------------------------------------------------------------------ template <class MutableBaseIterator> class proxy_iterator : public MutableBaseIterator { private: typedef proxy_iterator<MutableBaseIterator> self; typedef MutableBaseIterator base; typedef typename base::value_type::element_type T; public: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Types typedef typename base::difference_type difference_type; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors proxy_iterator() {} proxy_iterator(const MutableBaseIterator& it) : base(it) {} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Standard operators for iterators T* operator-> () {return (**this).get();} // Indirect self& operator++ () {base::operator++ (); return *this;} self& operator-- () {base::operator-- (); return *this;} self operator++ (int) {return base::operator++ (0);} self operator-- (int) {return base::operator-- (0);} self& operator+= (difference_type n) {base::operator+= (n); return *this;} self& operator-= (difference_type n) {base::operator-= (n); return *this;} self operator+ (difference_type n) const {return base::operator+ (n);} self operator- (difference_type n) const {return base::operator- (n);} friend self operator+ (difference_type n, self x) {return x + n;} friend difference_type operator- (self x, self y) {return base(x) - base(y);} }; //================================================================== // 'const_proxy_iterator' //------------------------------------------------------------------ { // Analagous to proxy_iterator, and with a conversion constructor from proxy_iterator. ... }; //================================================================== // 'proxy_container' //------------------------------------------------------------------ template <class BaseProxyContainer> class proxy_container : public BaseProxyContainer { private: typedef proxy_container self; typedef BaseProxyContainer base; typedef typename BaseProxyContainer::value_type value_type; typedef typename BaseProxyContainer::value_type::element_type T; public: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors proxy_container() {} proxy_container(const base& c) : base(c) {} template <class InputIterator> proxy_container(InputIterator first, InputIterator last) : base(first, last) {} explicit proxy_container(typename base::size_type n, const value_type& x = value_type()) : base(n, x) {} proxy_container(typename base::size_type n, const T& x) { for (typename base::size_type i = 0; i < n; ++i) { push_back(proxy<T>(new T(x))); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Iterators redefined typedef proxy_iterator<typename base::iterator> iterator; typedef const_proxy_iterator<typename base::iterator, typename base::const_iterator> const_iterator; typedef std::reverse_iterator<iterator> reverse_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Container members that either return an iterator or take a value_type parameter (or both) iterator begin() {return base::begin();} const_iterator begin() const {return base::begin();} iterator end() {return base::end();} const_iterator end() const {return base::end();} reverse_iterator rbegin() {return base::rbegin();} const_reverse_iterator rbegin() const {return base::rbegin();} reverse_iterator rend() {return base::rend();} const_reverse_iterator rend() const {return base::rend();} void push_front(const value_type& x) {base::push_front(x);} void push_front(const T& x) {base::push_front(proxy<T>(new T(x)));} void push_back(const value_type& x) {base::push_back(x);} void push_back(const T& x) {base::push_back(proxy<T>(new T(x)));} iterator insert(iterator position, const value_type& x) {return base::insert(x);} iterator insert(iterator position, const T& x) {return base::insert(position, proxy<T>(new T(x)));} void insert(iterator position, typename base::size_type n, const value_type& x) {base::insert(position, n, x);} void insert(iterator position, typename base::size_type n, const T& x) ; // Not yet implemented template<typename InputIterator> void insert(iterator position, InputIterator first, InputIterator last) {base::insert(position, first, last);} iterator erase(iterator position) {return base::erase(position);} iterator erase(iterator first, iterator last) {return base::erase(first, last);} void resize(typename base::size_type new_size, const value_type& x) {base::resize(new_size, x);} void resize(typename base::size_type new_size, const T& x) ; // Not yet implemented // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Special list operations void remove(const value_type& value) {base::remove(value);} void remove(const T& value); // Not yet implemented }; //================================================================== // The standard contaiers //------------------------------------------------------------------ template <class T> struct proxy_vector #if !defined(BOOST_MSVC_STD_ITERATOR) {typedef proxy_container< std::vector< proxy<T> > > self;}; #else {typedef proxy_container< std::deque< proxy<T> > > self;}; // MSVC workaround #endif template <class T> struct proxy_deque {typedef proxy_container< std::deque< proxy<T> > > self;}; template <class T> struct proxy_list {typedef proxy_container< std::list< proxy<T> > > self;}; // Associative containers are not yet implemented

Christian Engström <christian.engstrom@glindra.org> writes:
Class proxy_iterator
To get operator-> working in the same way as for a Container<T>, we define the classes proxy_iterator and const_proxy_iterator.
A proxy_iterator is publicly derived from the iterator of the underlying Container< proxy<T> >.
And if that iterator happens to be a pointer?
It works just like standard iterator in all respects except one, which is that operator-> does a double dereferencing, so that it will deliver a pointer to the T object itself. This lets us use the -> operator in the same way that we would with a direct container. operator* is not changed, however, and retains its single dereferencing semantics.
Are we really allowed to define these operators like this? --Yes, we are.
No you're not. The standard makes it very clear that the semantics of an iterator's operator-> must correspond to those of its operator*. The design makes several other wrong assumptions about iterator requirements. I suggest checking out the Boost iterator library for correct iterator construction. indirect_iterator may already suit your needs. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Christian Engström <christian.engstrom@glindra.org> writes:
A proxy_iterator is publicly derived from the iterator of the underlying Container< proxy<T> >.
And if that iterator happens to be a pointer?
Then of course it doesn't work. But is the container really STL compliant if it has ordinary pointers as iterators? If the answer to this question is yes, the requirement that the underlying iterator type must be a class should of course be added to the documentation, but apart from that I see no problem with it.
Are we really allowed to define these operators like this? --Yes, we are.
No you're not. The standard makes it very clear that the semantics of an iterator's operator-> must correspond to those of its operator*.
Or else what? Is there anything particular that the standard says will break if the two operators have different semantics? In Bjarne Stroustrup's "The Design and Evolution of C++", he says on page 241 (in the edition that I have): "For ordinary pointers, use of -> is synonymous with some uses of unary * and []. For example, for a Y* p it holds that: p->m == (*p).m == p[0].m As usual, no such guarantee is provided for user-defined operator. The equivalence can be provided when desired: [...]" The way I read it, the phrase "can ... when desired" does not have the same meaning as "must always". Am I misunderstanding something or is Bjarne Stroustrup wrong?
The design makes several other wrong assumptions about iterator requirements.
If you or somebody else can point them out to me I shall be very grateful.
indirect_iterator may already suit your needs.
Does indirect_iterator make possible to convert a program that has been written to use a direct container to use an indirect container instead by changing a single typedef? /Christian

Christian Engström <christian.engstrom@glindra.org> writes:
David Abrahams wrote:
Christian Engström <christian.engstrom@glindra.org> writes:
A proxy_iterator is publicly derived from the iterator of the underlying Container< proxy<T> >.
And if that iterator happens to be a pointer?
Then of course it doesn't work. But is the container really STL compliant if it has ordinary pointers as iterators?
Yes. Many std::vector implementations do that.
If the answer to this question is yes, the requirement that the underlying iterator type must be a class should of course be added to the documentation, but apart from that I see no problem with it.
Are we really allowed to define these operators like this? --Yes, we are. No you're not. The standard makes it very clear that the semantics of an iterator's operator-> must correspond to those of its operator*.
Or else what?
Or else the class doesn't satisfy the iterator requirements.
Is there anything particular that the standard says will break if the two operators have different semantics?
Technically, anything that claims to depend on a parameter matching the iterator concept. Less, technically, any generic algorithm that takes advantage of an iterator's operator->. The fact that the ones in the standard library don't use operator-> doesn't mean very much, since other peoples' algorithms are allowed to rely on iterators matching the standard iterator requirements.
In Bjarne Stroustrup's "The Design and Evolution of C++", he says on page 241 (in the edition that I have):
"For ordinary pointers, use of -> is synonymous with some uses of unary * and []. For example, for a Y* p it holds that: p->m == (*p).m == p[0].m As usual, no such guarantee is provided for user-defined operator. The equivalence can be provided when desired: [...]"
The way I read it, the phrase "can ... when desired" does not have the same meaning as "must always". Am I misunderstanding something or is Bjarne Stroustrup wrong?
Sure you can do whatever you want. You just can't call it an iterator (in C++). The standard library defines what an iterator is.
The design makes several other wrong assumptions about iterator requirements.
If you or somebody else can point them out to me I shall be very grateful.
I don't have time to look for all of them, but one assumption it makes is that the underlying iterator supplies value_type, iterator_category, etc. via nested public types as opposed to a specialization of std::iterator_traits. Writing correct iterators is really hard; that's part of the reason for the boost iterators library.
indirect_iterator may already suit your needs.
Does indirect_iterator make possible to convert a program that has been written to use a direct container to use an indirect container instead by changing a single typedef?
Not by itself, but it does most of the work for you. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Christian Engström <christian.engstrom@glindra.org> writes:
Christian Engström <christian.engstrom@glindra.org> writes:
But is the container really STL compliant if it has ordinary pointers as iterators?
David Abrahams wrote: Yes. Many std::vector implementations do that.
Right, this is exactly the kind of feedback I am looking for. I'll amend the documentation so that it states as an additional requirement that the iterators for the underlying container must be classes.
Are we really allowed to define these operators like this? --Yes, we are.
No you're not. [...]
Or else what?
Or else the class doesn't satisfy the iterator requirements.
Okay, I stand corrected. Adding the following to the documentation should fix the problem: "A proxie_iterator does not fulfill the formal criteria for being an iterator according to the definitions in the STL, since the relation it->m == (*it).m == it[0].m does not hold true. The expression it->m has the same semantics as it would for a Container<T>, whereas (*it).m and it[0].m have the semantics they would have for a Container< proxy<T> >. This means that the proxy_iterators may not work with algorithms that rely on any of these expressions. However, since none of the algorithms in the STL library do, all of the STL algorithms can be used without restriction."
Technically, anything that claims to depend on a parameter matching the iterator concept. Less, technically, any generic algorithm that takes advantage of an iterator's operator->. The fact that the ones in the standard library don't use operator-> doesn't mean very much, since other peoples' algorithms are allowed to rely on iterators matching the standard iterator requirements.
See above.
[...]
Sure you can do whatever you want.
Excellent, I think we are beginning to speak the same language ;-)
You just can't call it an iterator (in C++). The standard library defines what an iterator is.
Right, I won't claim that it's an iterator in that sense.
The design makes several other wrong assumptions about iterator requirements.
If you or somebody else can point them out to me I shall be very grateful.
I don't have time to look for all of them, but one assumption it makes is that the underlying iterator supplies value_type, iterator_category, etc. via nested public types as opposed to a specialization of std::iterator_traits.
Quite agree, there is nothing in the design as such that demands that implementation, I just did it that way because I don't know how to make it properly according to modern standards.
Writing correct iterators is really hard; that's part of the reason for the boost iterators library.
I know; that's part of the reason why I posted it here Boost ;-)

Christian Engström <christian.engstrom@glindra.org> writes:
David Abrahams wrote:
Christian Engström <christian.engstrom@glindra.org> writes:
Christian Engström <christian.engstrom@glindra.org> writes:
But is the container really STL compliant if it has ordinary pointers as iterators?
David Abrahams wrote: Yes. Many std::vector implementations do that.
Right, this is exactly the kind of feedback I am looking for. I'll amend the documentation so that it states as an additional requirement that the iterators for the underlying container must be classes.
I think you'll find there's little interest in such a component.
Are we really allowed to define these operators like this? --Yes, we are.
No you're not. [...]
Or else what? Or else the class doesn't satisfy the iterator requirements.
Okay, I stand corrected. Adding the following to the documentation should fix the problem:
"A proxie_iterator does not fulfill the formal criteria for being an iterator according to the definitions in the STL
I think you'll find there's little interest in such a component.
, since the relation
it->m == (*it).m == it[0].m
does not hold true. The expression it->m has the same semantics as it would for a Container<T>, whereas (*it).m and it[0].m have the semantics they would have for a Container< proxy<T> >.
This means that the proxy_iterators may not work with algorithms that rely on any of these expressions. However, since none of the algorithms in the STL library do
You have no guarantee of that. They are allowed to call operator-> on their iterators. Can't do much with them, but it's allowed.
, all of the STL algorithms can be used without restriction."
False. They are allowed to contain concept checks that cause compilation to fail if you pass them non-conforming iterators.
Technically, anything that claims to depend on a parameter matching the iterator concept. Less, technically, any generic algorithm that takes advantage of an iterator's operator->. The fact that the ones in the standard library don't use operator-> doesn't mean very much, since other peoples' algorithms are allowed to rely on iterators matching the standard iterator requirements.
See above.
[...] Sure you can do whatever you want.
Excellent, I think we are beginning to speak the same language ;-)
You just can't call it an iterator (in C++). The standard library defines what an iterator is.
Right, I won't claim that it's an iterator in that sense.
The design makes several other wrong assumptions about iterator requirements.
If you or somebody else can point them out to me I shall be very grateful. I don't have time to look for all of them, but one assumption it makes is that the underlying iterator supplies value_type, iterator_category, etc. via nested public types as opposed to a specialization of std::iterator_traits.
Quite agree, there is nothing in the design as such that demands that implementation, I just did it that way because I don't know how to make it properly according to modern standards.
I think you'll find there's little interest in such a component.
Writing correct iterators is really hard; that's part of the reason for the boost iterators library.
I know; that's part of the reason why I posted it here Boost ;-)
Cheers, -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:u1xopqx1v.fsf@boost-consulting.com... Christian Engström <christian.engstrom@glindra.org> writes: [snip]
, all of the STL algorithms can be used without restriction."
False. They are allowed to contain concept checks that cause compilation to fail if you pass them non-conforming iterators.
I just ran into this moving a project from VC6.5 to VC7.1, where home-grown pre-boost::iterator_adaptors were no longer valid for use with several algorithms. It was very easy(and appealing) to replace them with their Specialized Adaptor equivalents. Jeff F

Christian Engström <christian.engstrom@glindra.org> writes: , all of the STL algorithms can be used without restriction."
"David Abrahams" <dave@boost-consulting.com> wrote in message False. They are allowed to contain concept checks that cause compilation to fail if you pass them non-conforming iterators.
Jeff Flinn wrote: I just ran into this moving a project from VC6.5 to VC7.1, where home-grown pre-boost::iterator_adaptors were no longer valid for use with several algorithms. It was very easy(and appealing) to replace them with their Specialized Adaptor equivalents.
Jeff F
Yes, faulty implementations of underlying components can always be a problem. I don't know if this was the case in the situation you describe, but generally speaking, an algorithm that performs concept checks for concepts that it doesn't actually need is just that: faulty. So if (which has not been shown, but anyway) there are algortithm implementations that would not accept proxy_iterator:s because they do over-reaching pre-condition testing, this is a problem with that algorithm, not with the proxy_iterator. But I agree, it's a point well worth keeping in mind, that since the proxy package is designed in a way that may not coincide with many people's expectations, it may possibly run a higher than normal risk of triggering latent bugs in various third-party components. /Christian

Christian Engström <christian.engstrom@glindra.org> writes:
Christian Engström <christian.engstrom@glindra.org> writes: , all of the STL algorithms can be used without restriction."
"David Abrahams" <dave@boost-consulting.com> wrote in message False. They are allowed to contain concept checks that cause compilation to fail if you pass them non-conforming iterators. Jeff Flinn wrote: I just ran into this moving a project from VC6.5 to VC7.1, where home-grown pre-boost::iterator_adaptors were no longer valid for use with several algorithms. It was very easy(and appealing) to replace them with their Specialized Adaptor equivalents. Jeff F
Yes, faulty implementations of underlying components can always be a problem. I don't know if this was the case in the situation you describe, but generally speaking, an algorithm that performs concept checks for concepts that it doesn't actually need is just that: faulty.
The implementation is not faulty. The algorithm's implementation cannot be called faulty just because it exercises a part of the parameter's interface that's required by the algorithm's specification. You could make an argument that the specification is faulty because it only contains a concept "iterator" and not a less-refined concept "iterator that may not support the usual operator-> semantics". I think it's unreasonable to expect every concept to be broken down to its bare minimum to avoid placing any unneeded requirements on parameters. Meta-comment: you seem to have little respect for the value of specifications.
So if (which has not been shown, but anyway) there are algortithm implementations that would not accept proxy_iterator:s because they do over-reaching pre-condition testing, this is a problem with that algorithm, not with the proxy_iterator.
But I agree, it's a point well worth keeping in mind, that since the proxy package is designed in a way that may not coincide with many people's expectations, it may possibly run a higher than normal risk of triggering latent bugs in various third-party components.
We're not talking about bugs, and we're not talking about third-party components. We're talking about standard-conforming implementations of the standard library. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Christian Engström <christian.engstrom@glindra.org> writes:
[...], an algorithm that performs concept checks for concepts that it doesn't actually need is just that: faulty.
"David Abrahams" <dave@boost-consulting.com> wrote
The implementation is not faulty. The algorithm's implementation cannot be called faulty just because it exercises a part of the parameter's interface that's required by the algorithm's specification.
True, it's the specification that's faulty in that case, not the implementation as such.
You could make an argument that the specification is faulty because it only contains a concept "iterator" and not a less-refined concept "iterator that may not support the usual operator-> semantics".
If that is how the specification in STL is written, I am indeed making that argument. All the STL algorithms are guaranteed to work on containers of built-in types, such as a Container<int>, which does not even have operator->, let alone any particular semantics for it. If there is a "specification" somewhere that claims that operator-> must be defined in any particular way for the algorithms to work, even though this is obviously not true, then I consider that specification to be faulty.
I think it's unreasonable to expect every concept to be broken down to its bare minimum to avoid placing any unneeded requirements on parameters.
What an unusual position to adopt. What do you propose then, actual requirements plus 10%? Plus 20%?
Meta-comment: you seem to have little respect for the value of specifications.
If we are talking about "specifications" that do not accuratetely describe the actual requirements of the algorithm, I indeed have very little respect for them, which seems to be exactly the amount they deserve. But if we are talking about real, accurate specifications, I have the deepest respect for them. It is for this reason that I am so very interested in finding any cases where a proxy_iterator would not work properly, but an "ordinary" iterator would, so that I can add them to the specifications for the proxy_iterator.
But I agree, it's a point well worth keeping in mind, that since the proxy package is designed in a way that may not coincide with many people's expectations, it may possibly run a higher than normal risk of triggering latent bugs in various third-party components.
We're not talking about bugs, and we're not talking about third-party components. We're talking about standard-conforming implementations of the standard library.
Okay, so a more accurate way of describing the problem would be: "Due to a glitch in the formal specification for iterators in STL, algorithms are allowed to place semantic restrictions on the -> operator when it exists, even though the algorithm can never acutally use the operator in question, since is not actually required to exist at all. The proxy_iterator:s may not work with STL implementations that deliberately take advantage of this glitch to do over-reacing concept-checking for concepts that they do not actually require. It is not known if any such implementations actually exist or are in common use." /Christian

I seriously have to wonder if I'm being trolled, here... Christian Engström <christian.engstrom@glindra.org> writes:
Okay, so a more accurate way of describing the problem would be:
"Due to a glitch in the formal specification for iterators in STL, algorithms are allowed to place semantic restrictions on the -> operator when it exists, even though the algorithm can never acutally use the operator in question, since is not actually required to exist at all.
And what about a third-party algorithm that iterates over classes and happens to use operator-> ? That's no bug?
The proxy_iterator:s may not work with STL implementations that deliberately take advantage of this glitch to do over-reacing concept-checking for concepts that they do not actually require. It is not known if any such implementations actually exist or are in common use."
Are you trying to write a library (if not, this whole discussion is OT), or is this just for yourself? How long do you think this list of exceptions has to to be before the component is of no interest to anyone other than yourself? After you responded to "it's not an iterator" with one caveat, my eyes glazed over (not to mention, "I just did it that way because I don't know how to make it properly according to modern standards"). I'm sure it won't take most people much longer to realize that it's obviously not built "properly according to modern standards" when they see a list of 3 or more subtle/not-so-subtle ways in which it isn't an iterator. It took me about 3 seconds to notice that your iterator is broken in another way: it assumes the operators on the underlying iterators are implemented as member functions and not free functions. I have no idea how many more of these issues are lurking. Why you wouldn't just build a conforming iterator the easy way is beyond me. I think I'm all done here; good luck in the future. Cheers, Dave -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote: I seriously have to wonder if I'm being trolled, here...
No, you're not being trolled, you're being presented with a design idea that is new and uses the C++ language in unconventional ways. I know that the design violates many of the rules of thumb that C++ programmers (quite sensibly) normally choose to follow, which makes it very easy to fall into the trap of rejecting it outright, without actually taking the time to evaluate it properly to see if it is in fact consistent after all. I'll immediately concede that the design *is* very bold and unusual, and that it could well be the case that it does in fact contain some fatal inconsistency that invalidates it. But at least so far, I note that neither you nor anybody else has been able to point at such a flaw in the design, so at least for the moment, I think that your dismissive tone is uncalled for. I have in good faith presented an idea that has an obvious bearing on library design if it does turn out to be solid. It is yet too early to tell if this is the case or not, but until you or somebody else has come up with a more substantive objection than "I don't think many people will like it", I don't see why I should have to accept being called a troll just for suggesting it.
Christian Engström <christian.engstrom@glindra.org> writes:
Okay, so a more accurate way of describing the problem would be:
"Due to a glitch in the formal specification for iterators in STL, algorithms are allowed to place semantic restrictions on the -> operator when it exists, even though the algorithm can never acutally use the operator in question, since is not actually required to exist at all.
David Abrahams wrote:
And what about a third-party algorithm that iterates over classes and happens to use operator-> ? That's no bug?
Does this mean that we are now in agreement that all of the STL algorithms will in fact work with proxy_iterator:s, despite your earlier reservations? Regarding other algorithms that iterate over the objects container and use the -> operator, they will work the same with proxy iterators on a proxy_container as they would with ordinary iterators on a direct container --- that's the whole point of the design. So no, that's not a bug.
Are you trying to write a library (if not, this whole discussion is OT), or is this just for yourself?
As stated above, I am trying to present a design idea that I hope might be useful in the context of building general libraries, like the ones in Boost. I am confident that you are not trying to suggest that that kind of discussion is OT on the Boost list, even if you personally happen to dislike the idea as such. Whether it will be just for myself or for others as well depends on whether somebody else will find the idea interesting enough to explore it further. If so, they are free to use the idea as such and/or the files I posted in the Yahoo files section in whatever way they want, including writing a library based on the design and submitting it to Boost. If not, it will just be for myself, and then of course I won't discuss it any further in this forum.
How long do you think this list of exceptions has to to be before the component is of no interest to anyone other than yourself? After you responded to "it's not an iterator" with one caveat, my eyes glazed over (not to mention, "I just did it that way because I don't know how to make it properly according to modern standards"). I'm sure it won't take most people much longer to realize that it's obviously not built "properly according to modern standards" when they see a list of 3 or more subtle/not-so-subtle ways in which it isn't an iterator.
As I very explicitly said in my very first post, I am presenting a design idea for discussion, together with a draft implementation that serves to illustrate that idea. The use of the word "draft" was deliberate, and was intended to convey the message that the implementation did not claim to be ready to be put in production in its current state, but that my primary interest at this point in time was not to examine the various implementation details, but to discuss the validity of the underlying design idea. So far, all of your objections that could not be solved by clarifying the documentation have been related to implementation details, and not the design as such. (Except, of course, your repeated assertion that you don't think many people will be interested in the idea, but I don't really count that as a particularly technical point.) While your observations on the shortcomings of the implementation are valid and true as such, they don't relate to the validity of the design, so unless you have already decided to go ahead and write a production grade implementation (which of course you quite obviously haven't :-) ), they are at the very least somewhat premature at this moment. However, as they are completely valid and quite valuable from an implementation perspective, I am nevertheless grateful to you for taking the time to make them.
It took me about 3 seconds to notice that your iterator is broken in another way: it assumes the operators on the underlying iterators are implemented as member functions and not free functions.
Good point, but again it relates to the draft implementation, and not to the underlying design.
Why you wouldn't just build a conforming iterator the easy way is beyond me.
Because the traditional container interfaces do not offer the convenience of being able to provide the same interface for indirect and direct containers, and to make it possible to switch between them by changing a single typedef. If it turns out to be possible to write a library that allows users to do this, whatever effort went designing the library will be well worth it because of the time such a library would save for its users. This is the same underlying arithmetic that serves as the foundation for any library, including all the libraries that are currently part of Boost. I am very saddened by the hostile tone that I think you have chosen to adopt in this discussion, and quite frankly rather surprised by it, since I know that this is not how you normally prefer to carry yourself on this list. It's just an unusual idea that may or may not be valid, that's all --- it's not an attack on anything or anybody, so there's no need to get upset. If you don't like the idea you don't have to, but I really can't see why this means that you should have accuse me of trolling or question the sincerity of my motives for presenting it on the Boost list. /Christian

Christian Engström <christian.engstrom@glindra.org> writes:
David Abrahams wrote: I seriously have to wonder if I'm being trolled, here...
No, you're not being trolled, you're being presented with a design idea that is new and uses the C++ language in unconventional ways.
Not really; it just uses the term "iterator" in unconventional ways.
I know that the design violates many of the rules of thumb that C++ programmers (quite sensibly) normally choose to follow, which makes it very easy to fall into the trap of rejecting it outright, without actually taking the time to evaluate it properly to see if it is in fact consistent after all.
I'll immediately concede that the design *is* very bold and unusual, and that it could well be the case that it does in fact contain some fatal inconsistency that invalidates it.
Whether the problems are fatal or not clearly depends on how many caveats you're willing to tolerate.
But at least so far, I note that neither you nor anybody else has been able to point at such a flaw in the design
The flaws I pointed out are fatal from my point of view.
, so at least for the moment, I think that your dismissive tone is uncalled for.
I tried to help you improve the component; you dismissed my suggestion that you use indirect_iterator.
I have in good faith presented an idea that has an obvious bearing on library design if it does turn out to be solid. It is yet too early to tell if this is the case or not, but until you or somebody else has come up with a more substantive objection than "I don't think many people will like it",
You clearly don't think any of my technical objections are substantive. I am done looking for new ways to object.
I don't see why I should have to accept being called a troll just for suggesting it.
I didn't call you a troll. I honestly wondered if that was happening to me.
Christian Engström <christian.engstrom@glindra.org> writes: Okay, so a more accurate way of describing the problem would be:
"Due to a glitch in the formal specification for iterators in STL, algorithms are allowed to place semantic restrictions on the -> operator when it exists, even though the algorithm can never acutally use the operator in question, since is not actually required to exist at all.
David Abrahams wrote: And what about a third-party algorithm that iterates over classes and happens to use operator-> ? That's no bug?
Does this mean that we are now in agreement that all of the STL algorithms will in fact work with proxy_iterator:s, despite your earlier reservations?
No. I don't know what gave you that idea. It may or may not be true for existing implementations of the algorithms, but that's not the same question as whether "the algorithms work with proxy_iterators".
Regarding other algorithms that iterate over the objects container and use the -> operator, they will work the same with proxy iterators on a proxy_container as they would with ordinary iterators on a direct container --- that's the whole point of the design.
Not if they make the allowed assumption that p->x and (*p).x have corresponding meanings.
Are you trying to write a library (if not, this whole discussion is OT), or is this just for yourself?
As stated above, I am trying to present a design idea that I hope might be useful in the context of building general libraries, like the ones in Boost. I am confident that you are not trying to suggest that that kind of discussion is OT on the Boost list, even if you personally happen to dislike the idea as such.
No I am not.
Whether it will be just for myself or for others as well depends on whether somebody else will find the idea interesting enough to explore it further. If so, they are free to use the idea as such and/or the files I posted in the Yahoo files section in whatever way they want, including writing a library based on the design and submitting it to Boost. If not, it will just be for myself, and then of course I won't discuss it any further in this forum.
OK, I'm done discussing this, so we can see if anyone else says they're interested. <snip>
So far, all of your objections that could not be solved by clarifying the documentation have been related to implementation details, and not the design as such. (Except, of course, your repeated assertion that you don't think many people will be interested in the idea, but I don't really count that as a particularly technical point.)
You can "solve" any arbitrarily bad problem with any component by documenting the limitation that you can't use it that way.
It took me about 3 seconds to notice that your iterator is broken in another way: it assumes the operators on the underlying iterators are implemented as member functions and not free functions.
Good point, but again it relates to the draft implementation, and not to the underlying design.
Suggestion: ask yourself why this objection is an implementation detail, while the inability to "proxy-ize" a pointer is not. Both are soluble problems.
Why you wouldn't just build a conforming iterator the easy way is beyond me.
Because the traditional container interfaces
I wasn't talking about container interfaces. I was talking about iterators.
do not offer the convenience of being able to provide the same interface for indirect and direct containers, and to make it possible to switch between them by changing a single typedef.
You obviously haven't looked carefully at indirect_iterator. If I didn't think these capabilities were valuable, I wouldn't have written it. In fact, it was the original motivation for the whole iterator adaptors library. <snip>
I am very saddened by the hostile tone that I think you have chosen to adopt in this discussion, and quite frankly rather surprised by it, since I know that this is not how you normally prefer to carry yourself on this list.
I don't know what I did to make you think I was hostile, but whatever it was, I apologize. And that's really the only reason I took the time to respond to your post.
It's just an unusual idea that may or may not be valid, that's all --- it's not an attack on anything or anybody, so there's no need to get upset. If you don't like the idea you don't have to, but I really can't see why this means that you should have accuse me of trolling or question the sincerity of my motives for presenting it on the Boost list.
I wondered whether I was being trolled because I found myself compulsively wasting lots of time (time I can't afford to spend) trying to convince you of various things I consider obvious, and making no progress. I began to think someone was just yanking my chain. Cheers, and good luck with your design. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David, Thank you for your answer. I fully respect that you don't want to spend any more time on an idea that you don't like and don't believe in, so I don't really expect you to respond to this post, but I still want to comment on the technical point you make in case somebody else is reading this thread, now or in the future.
David Abrahams wrote:
It took me about 3 seconds to notice that your iterator is broken in another way: it assumes the operators on the underlying iterators are implemented as member functions and not free functions.
Christian Engström <christian.engstrom@glindra.org> writes:
Good point, but again it relates to the draft implementation, and not to the underlying design.
Suggestion: ask yourself why this objection is an implementation detail, while the inability to "proxy-ize" a pointer is not. Both are soluble problems.
The reason why I consider them different is that the objection about the operators as member functions is trivial to correct (once the flaw has been pointed out, which is less trivial), by just modifying the respective member function bodies in the proxy iterators. The inability to proxy-ize a pointer, on the other hand, is due to the fact that the proxy iterators are derived from the underlying iterators, which of course rules out raw pointers. The reason why they are derived, in turn, is that we want the proxy iterators and the underlying "normal" iterators to be implicitly convertable to each other in both directions. If both conversions were user defined, as they would have to be if the proxy iterators could not utilize the implicit derived-to-base conversion, this would lead to a host of ambiguity problems, since the compiler has no reason to prefer one conversion over the other. The classic example is the expression a == b, which becomes ambiguous because the compiler is just as happy to carry out the comparison as either b_type(a) == b or a == a_type(b) if both conversions are available as implicit user defined conversions, and there is no == operator defined for the mixed types case. If one of the conversions is instead a derived-to-base conversion and the the other is user defined, the compiler will prefer the derived-to-base conversion, so there is no ambiguity. Even if the particular example of the == operator can be handled by using the excellent boost/operators.hpp package to define a mixed type == operator, it will at the very least introduce a new level of complexity into the design, even if it were to turn out to be possible to handle the ambiguity problem in general by going further along this route. For this reason I prefer to accept the limitation that the container adaptor can only be used with STL compatible containers that implement the iterators as classes rather than as raw pointers. If someone can demonstrate an amended design that works with raw pointers as well, that might of course be even better, but even without that generalization, I think that an adaptor that works on the many containers that do in fact have class iterators still covers a quite respectable domain, so I don't see it as an absolute necessity to expand it any further. /Christian

Christian Engström <christian.engstrom@glindra.org> writes:
The reason why they are derived, in turn, is that we want the proxy iterators and the underlying "normal" iterators to be implicitly convertable to each other in both directions. If both conversions were user defined, as they would have to be if the proxy iterators could not utilize the implicit derived-to-base conversion, this would lead to a host of ambiguity problems, since the compiler has no reason to prefer one conversion over the other.
The classic example is the expression a == b, which becomes ambiguous because the compiler is just as happy to carry out the comparison as either b_type(a) == b or a == a_type(b) if both conversions are available as implicit user defined conversions, and there is no == operator defined for the mixed types case.
A classic example of a problem that can bve solved more elegantly by the iterator adaptors library, *without* the problematic two-way implicit conversion -- and it *is* problematic, even if one direction is derived-to-base. You asked for feedback; I say look at the work that those came before you already did in this area. I don't know why you won't, but I've stopped trying now. Cheers, -- Dave Abrahams Boost Consulting www.boost-consulting.com

Christian Engström <christian.engstrom@glindra.org> writes:
The classic example is the expression a == b, which becomes ambiguous [...]
David Abrahams wrote:
A classic example of a problem that can bve solved more elegantly by the iterator adaptors library, *without* the problematic two-way implicit conversion -- and it *is* problematic, even if one direction is derived-to-base. You asked for feedback; I say look at the work that those came before you already did in this area. I don't know why you won't, but I've stopped trying now.
Excellent, if there is a more elegant way that can even handle raw pointer iterators, that could very well be how the proxy package should be implemented --- *if* it should be implemented. But there's the rub, as I see it. I get the distinct feeling that we are talking a little bit at cross-purposes, which is making this discussion much more frustrating for both of us than it ought to be. Before we've managed to give each other a heart attack :-), I'd like to try and see if I can explain what I mean. If I read you correctly, you are getting more and more annoyed at the idio^h^h^h^h list member who persists in defending some crappy home-made indirect container implementation, while [seemingly] refusing to even look at the components that already exist in the form of professionally crafted state-of-the-art implementations in the Boost library. Even if one has never been in such a situation (which I have, in other areas), it's easy to imagine how frustrating it must be. For me, what's so frustrating is that although the discussion is both useful and interesting for me, and that I'm learning a lot, I feel that it is always drifting away from the aspect that I'm really the most interested in right now, which is to find an answer to the question: --Is there a way to define a package for indirect containers, so that one can take a program that uses a direct container, and convert it to using an indirect container just by changing the definition of the container type? I have a first proposal for a design and a demo implementation (that's a better word, isn't it?) that seem to suggest, to me at least, that the answer is "yes", but before doing anything else I'd like to hear some further opinions on the matter. Is the apparent "yes" of the demo implementation false, and if so, what is the problem that makes it impossible to construct a solution along these general lines? When you ask me why I don't just use indirect_iterator, the answer is that it's because I can't see how I could achieve my goal of "just change the typedef to switch" by using it. Are you saying that it's actually possible to use indirect_iterator to get the test program from my first post to switch from using a direct to an indirect container, by just changing a single line in the program? If so, could you please tell me which line should be changed to what, because I honestly can't see it for myself? I enclose a copy of the test program at the bottom of this post for your convenience. If it's *not* possible to do this with indirect_iterator, can we at least agree that the proxy package strives (whether it succeeds or not) to achieve something that is not already offered by the existing packages? If we were to agree on this, the next question would of course be "So what?", or in other words: --Would it be any useful to be able to switch containers like that? The problem here is of course that "usefulness" is very much in the eye of the beholder, so all that I can say with certainty is that I personally would find it quite useful, and that I don't see why other people as well shouldn't sometimes find themselves in situations when they would want to change container types easily. After all, isn't one of the great benefits of the STL containers that they have (reasonably) uniform interfaces, so that it's easy to switch between the different container types, often by just changing a typedef? Whether others agree that it's a worthwhile quest or not, this is what I'm looking for, and this is what I haven't been able to find in the existing packages. If there was such a package, I would of course prefer it if there were as few restrictions as possible on when and how it could be used, but since I'm aware that this is the real world, I am prepared to accept it if there are certain caveats and restrictions as long as they can be clearly documented, and as long as the situations where the package *can* be used still represent a sizable portion of everyday use. I'll briefly state the main "claims" of the proxy package design, and try to summarize where I think it stands today, after the discussion here in this thread. 1) By using a proxy object, which is essentially a shared_ptr with the pointer arithmetic removed and with the comparison operators redefined to compare the elements, and storing such proxy objects in an STL compatible container, we can use all the algorithms in the STL on the Container< proxy<T> >, and get the same result as with a Container<T>. (This works even with the standard STL iterators and in the absence of any of the other components of the package, so it could be used as a standalone feature if desired.) 2) By making proxy<T> implicitly convertible to T&, a proxy<T> can be used (almost) as a T when it comes to assignments and argument passing. In the situations when it doesn't work, such as if a function takes a U argument and was expecting another user-defined conversion from T to U to let it handle a T arguments as well, the compiler will generate a type error. 3) By letting proxy_container define an extra variant for each member function that would normally take a proxy<T> argument, and letting the extra variant accept a T instead, we complete the last part of the symmetry with direct containers as far as entire T objects are concerned. 4) By defining operator-> in for proxy_container's iterators to do a double dereferencing, the meaning of that expression remains the same for a proxy_container iterator as it would for the direct container iterator. operator* keeps its normal semantics and does a single dereferencing. This definition is necessary to keep Claim 1 valid, and to keep the proxy_container working as one would expect from an indirect container in general. Can any of these claims be irreparably shot down? *That* is the question that I would like to have an answer to. Possible killers Your objection that the STL standard is phrased in a way that allows implementations to reject would-be iterators because of the semantics of an operator that won't be used and which doesn't even have to exist is indeed a serious one, but I would still be interested to know if there are any other potential show-stoppers as far as these claims are concerned. If this STL definition were to turn out that the *only* thing that makes it impossible to provide a "typedef switchable" container, I think that that in itself would be a very interesting thing to know. I understand your reluctance to add more finely-grained iterator categories than necessary to the standard, but if an otherwise meaningful design was broken solely by the fact that the STL lists requirements that exceed its actual needs, wouldn't that at the least be a serious argument for considering a revision of those requirements? One solution could perhaps be to introduce some "minimal iterator" categories that contained only what the STL algorithms that also work on built-in types actually need, and reduce the requirements for those algorithms accordingly, but otherwise keep everything as is? Anyway, I've noted the objection you make, but I'm still interested to know if this is the only one of this dignity, or if there are more. Restrictions and caveats On use: U1) The meaning of (*x).y and x->y is not the same for proxy_iterator, so if the program uses the (*x).y form, the package can't be used. U2) If the code that surrounds the container expects another user-defined conversion from T to U to take place, this won't work and the compiler will generate a type mismatch error, because only one implicit user-defined conversion is allowed, and that one has already been used up by the implicit conversion from proxy<T>. On the underlying container: C1) The underlying container must define its iterators as classes. If it turns out that this restriction can be lifted that would be great news, but if some unforeseen problem should arise with it, it's a restriction that I (personally) would be able to live with. In the demo implementation the package works directly for all the implemented classes except vector with MSVC6, and for that case I don't think the workaround of using a deque is unreasonable. I am fully aware that both the workaround and the implementation in general are crude and could be rewritten in a much more sophisticated way, but right now its only purpose in life is to demonstrate a design that is built on the four claims above. For that purpose it works with vector/deque/list under both gcc and MSVC6, so with all its faults, the demo already seems to cover what could possibly be the six most widely used containers in the world. Implementation issues Loads, no need to repeat them here, but all fixable it seems. But if there's a flaw in the basic design, it won't help a bit to implement it in the most sophisticated manner imaginable, the result will just be rubbish anyway. Since I am not at all entirely convinced yet that the four claims are as rock-steady as I'd like them to be, I would much rather discuss what further restrictions and caveats can be found against the claims as such, before going on to discuss things that could be fixed in the implementation. I find it very encouraging, however, that the impression I get from reading your comments is that the implementation issues are all entirely soluble, as long as it's done in the proper way. I hope this explains why I'm being so bone-headed. :-) /Christian //=================================================================== int main (int argc, char* argv[]) //------------------------------------------------------------------- { person a_person; person someone_special("Alice", 27); ifstream in_file("test_proxy.in"); typedef std::vector<person> buf_type; // <---- // typedef proxy_vector<person>::self buf_type; // <---- buf_type buf(5, person("Undefined", -1)); buf.front() = person("First", 1); *(buf.begin() + 1) = person("Second", 2); buf[2] = person("Third", 3); (buf.begin() + 3)->name = "Incomplete"; while (a_person.read(in_file)) { buf.push_back(a_person); } std::sort(buf.begin(), buf.end()); for (buf_type::iterator it = buf.begin(); it != buf.end(); ++it) { cout << *it; if (it->age < 0) cout << " Not a proper age"; if (*it == someone_special) cout << " Someone special"; if (it != buf.begin() && (*it == *(it – 1))) cout << " Duplicate record"; cout << endl; } buf_type::iterator end_it = std::unique(buf.begin(), buf.end()); cout << "\nNumber of unique records: " << (end_it - buf.begin()); cout << "\n\nCalls to 'person's copy constructor: " << calls_to_copy_constructor << endl; return 0; }

Christian Engström <christian.engstrom@glindra.org> writes:
Christian Engström <christian.engstrom@glindra.org> writes:
The classic example is the expression a == b, which becomes ambiguous [...] David Abrahams wrote: A classic example of a problem that can bve solved more elegantly by the iterator adaptors library, *without* the problematic two-way implicit conversion
On second thought; that particular "feature" is not provided by the library, but could be provided using the techniques in the library.
-- and it *is* problematic, even if one direction is derived-to-base. You asked for feedback; I say look at the work that those came before you already did in this area. I don't know why you won't, but I've stopped trying now.
Excellent, if there is a more elegant way that can even handle raw pointer iterators, that could very well be how the proxy package should be implemented --- *if* it should be implemented. But there's the rub, as I see it.
I get the distinct feeling that we are talking a little bit at cross-purposes, which is making this discussion much more frustrating for both of us than it ought to be.
Sorry to be blunt, but AFAICT, the problem is that you're not listening.
Before we've managed to give each other a heart attack :-), I'd like to try and see if I can explain what I mean.
If I read you correctly, you are getting more and more annoyed at the idio^h^h^h^h
You're not an idiot; you just ask for feedback and then don't follow up on it.
list member who persists in defending some crappy home-made indirect container implementation
I don't care about your crappy "container" (it isn't a container either, since containers have iterators). It's your crappy "iterator" I have a problem with. I would have thought that was abundantly clear by now.
For me, what's so frustrating is that although the discussion is both useful and interesting for me, and that I'm learning a lot, I feel that it is always drifting away from the aspect that I'm really the most interested in right now, which is to find an answer to the question:
--Is there a way to define a package for indirect containers, so that one can take a program that uses a direct container, and convert it to using an indirect container just by changing the definition of the container type?
I already told you that solving that problem was the very reason for inventing indirect_iterator, so it darned well better be possible. I have no time to read the rest of your post, sorry. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Hi Christian,
It can quite often be of interest to convert a program that uses a direct container into using an indirect container of smart pointers to the objects instead, either just temporarily to see what happens to performance, or permanently if it turns out to be beneficial.
I have been in situations where such an easy change would have been nice. So I think you have a valid problem. br Thorsten
participants (4)
-
Christian Engström
-
David Abrahams
-
Jeff Flinn
-
Thorsten Ottosen