c++.stlsoft - COM Iterator
- Javier Estrada (197/197) Jan 28 2004 Matthew,
- Matthew (23/220) Jan 28 2004 Hi Javier
Matthew, It's interesting how concepts are re-found, refined, enhanced...A couple of years ago I started looking for an STL-compliant iterator over an IEnumXxx. I found a couple of references on the internet that left much to be desired. Finally, I came accross COM-STL Bridge library from Ray Brown--the page does not exist anymore--from a book published by MS Press. He had interesting concepts for the iterators _extending_ the original IEnumXxx interface, and was able to provide caching and other concepts with his iterators. I was not so ambitious in that area, since I just needed something to use with the algorithms. For the most part, the iterators were what I needed, but they needed a couple of enhancements. I provided the enhancements and used them extensively in a telephony project. I tried to publish it with CUJ, too, but they were not interested :-( I'm including the code for an STL-compliant iterator, that I believe it's a little simpler to use than your approach. In particular, I do not use a container, but base the iteration on a range. The end of the iteration is found by using a singular iterator, similar to ostream_iterator. The usage is as follows: com_iterator<E, T, C> --------------------- where: E - Enumeration interface T - Enumerated type C - Iterator category (input or forward) An example: typedef com_iterator<IEnumXxx, Xxx> XxxIterator; // input iterator for Xxx ... XxxIterator first(pEnum); XxxIterator last; // singular iterator to mark end of range. for_each(first, last...); OR while(first != last) ; Here's some sample code of how I used it: // COM iterator support #include "com_iterator.h" // Useful typedefs. Require <tapi3.h> typedef com_iterator<IEnumAddress, ITAddress, std::forward_iterator_tag> tapi_address_iterator; typedef com_iterator<IEnumStream, ITStream, std::forward_iterator_tag> tapi_stream_iterator; ... CComPtr<IEnumAddress> pea; // Temporary variable to hold the TAPI address enumerator CComPtr<ITAddress> pa; // Temporary variable to hold the TAPI address being enumerated vector< CComPtr<ITAddress> > tapiAddresses; // Temporary vector to hold the tapiAddresses that are associated // with my domain criteria // Initialize the TAPI address enumerator m_pTapi->EnumerateAddresses(&pea); // Copy all the TAPI addresses that are associated... copy_if(tapi_address_iterator(pea), tapi_address_iterator(), back_inserter(tapiAddresses), my_functor_here(...)); I'd be interested in hearing from you, or if you think there's something useful that can be incorporated in STLSoft from the code, I'd be happy to contribute. You can reach me at: jestrada*at*developeer*dot*com Regards, Javier Estrada ----------------------------------------- #if !defined INCLUDED_COM_ITERATOR #define INCLUDED_COM_ITERATOR #include <comdef.h> #include <atlbase.h> #include <iterator> #include <cassert> #pragma once template <typename E, typename T, typename C = std::input_iterator_tag > class com_iterator : public std::iterator<C, T, ptrdiff_t, T*, T*&> { typedef std::iterator<C, T, ptrdiff_t, T*, T*&> base; typedef base::iterator_category iterator_category; typedef base::value_type value_type; typedef base::reference reference; typedef base::pointer pointer; public: com_iterator() : past_end(true) { // Construct a singular iterator } com_iterator(const com_iterator &rhs) { // Copy constructor past_end = rhs.past_end; if (!past_end) { // Do I use Clone or add ref? _copy(rhs.enumerator, iterator_category()); // Increment the ref count on the underlying enumerator... // enumerator = rhs.enumerator; // ...and the current value current = rhs.current; } } explicit com_iterator(E *e):past_end(false) { // Constructs from an enumerator _copy(e, iterator_category()); // enumerator = e; acquire(); } com_iterator& operator = (const com_iterator& rhs) { // Assignment operator if (*this == rhs) return *this; past_end = rhs.past_end; if (!past_end) { _copy(rhs.enumerator, iterator_category()); // ...and the current value current = rhs.current; } return *this; } ~com_iterator() { // Destructor } bool operator == (const com_iterator& rhs) const { return _equal(rhs, iterator_category()); } inline bool operator != (const com_iterator& rhs) const { // Invokes operator == return !(*this == rhs); } reference operator * () const { // Return designated value // Requires IEnumXxx::Next() assert(!past_end); return reference(CAdapt< CComPtr<T> >(current)); //return reference(current); } pointer operator -> () const { // Return designated value // Requires IEnumXxx::Next() return pointer(current); } com_iterator& operator ++ () { // Pre-increment acquire(); return *this; } com_iterator operator ++ (int) { // Post-increment com_iterator result(*this); acquire(); return result; } private: void _copy(const CComPtr<E> &e, std::input_iterator_tag) { enumerator = e; } void _copy(const CComPtr<E> &e, std::forward_iterator_tag) { enumerator = 0; // Release if necessary e->Clone(&enumerator); } bool _equal(const com_iterator& rhs, std::input_iterator_tag) const { // Two com_iterators compare equal when they're // past-the-end iterators. if (past_end && rhs.past_end) return true; if (past_end || rhs.past_end) return false; if (enumerator != rhs.enumerator) return false; return current == rhs.current; } bool _equal(const com_iterator& rhs, std::forward_iterator_tag) const { // Two com_iterators compare equal when they're // past-the-end iterators. if (past_end && rhs.past_end) return true; if (past_end || rhs.past_end) return false; return current == rhs.current; } void acquire() { // Reads the iterator value current = 0; // Release current value HRESULT hr = enumerator->Next(1, ¤t, 0); if (FAILED(hr) || hr == S_FALSE) { past_end = true; } } private: CComPtr<E> enumerator; // Underlying enumerator CComPtr<T> current; // Current value; bool past_end; // Maintains past-the-end status }; #endif // INCLUDED_COM_ITERATOR
Jan 28 2004
Hi Javier I've heard of CSB, but never seen it for the reasons you describe. If it's ok with you I'll check this out in a week or two, as I'm in the final review stage of my book at the moment. It's quite likely we might look at incorporating some of your techniques. :) You'll here from me soon. Cheers Matthew Wilson STLSoft moderator (http://www.stlsoft.org) Contributing editor, C/C++ Users Journal (www.synesis.com.au/articles.html#columns) "But if less is more, think how much more more will be!" -- Dr Frazier Crane ---------------------------------------------------------------------------- --- "Javier Estrada" <ljestrada hotmail.com> wrote in message news:bv98us$1mvn$1 digitaldaemon.com...Matthew, It's interesting how concepts are re-found, refined, enhanced...A coupleofyears ago I started looking for an STL-compliant iterator over anIEnumXxx.I found a couple of references on the internet that left much to bedesired.Finally, I came accross COM-STL Bridge library from Ray Brown--the pagedoesnot exist anymore--from a book published by MS Press. He had interesting concepts for the iterators _extending_ the original IEnumXxx interface, and was able to provide caching and other conceptswithhis iterators. I was not so ambitious in that area, since I just needed something to use with the algorithms. For the most part, the iterators were what I needed, but they needed a couple of enhancements. I provided the enhancements and used them extensively in a telephony project. I tried to publish it with CUJ, too, but they were not interested :-( I'm including the code for an STL-compliant iterator, that I believe it'salittle simpler to use than your approach. In particular, I do not use a container, but base the iteration on a range. The end of the iteration is found by using a singular iterator, similar to ostream_iterator. The usage is as follows: com_iterator<E, T, C> --------------------- where: E - Enumeration interface T - Enumerated type C - Iterator category (input or forward) An example: typedef com_iterator<IEnumXxx, Xxx> XxxIterator; // input iterator for Xxx ... XxxIterator first(pEnum); XxxIterator last; // singular iterator to mark end of range. for_each(first, last...); OR while(first != last) ; Here's some sample code of how I used it: // COM iterator support #include "com_iterator.h" // Useful typedefs. Require <tapi3.h> typedef com_iterator<IEnumAddress, ITAddress, std::forward_iterator_tag> tapi_address_iterator; typedef com_iterator<IEnumStream, ITStream, std::forward_iterator_tag> tapi_stream_iterator; ... CComPtr<IEnumAddress> pea; // Temporary variable to hold the TAPI address enumerator CComPtr<ITAddress> pa; // Temporary variable to hold the TAPI address being enumerated vector< CComPtr<ITAddress> > tapiAddresses; // Temporary vector to hold the tapiAddresses that are associated // with my domain criteria // Initialize the TAPI address enumerator m_pTapi->EnumerateAddresses(&pea); // Copy all the TAPI addresses that are associated... copy_if(tapi_address_iterator(pea), tapi_address_iterator(), back_inserter(tapiAddresses), my_functor_here(...)); I'd be interested in hearing from you, or if you think there's something useful that can be incorporated in STLSoft from the code, I'd be happy to contribute. You can reach me at: jestrada*at*developeer*dot*com Regards, Javier Estrada ----------------------------------------- #if !defined INCLUDED_COM_ITERATOR #define INCLUDED_COM_ITERATOR #include <comdef.h> #include <atlbase.h> #include <iterator> #include <cassert> #pragma once template <typename E, typename T, typename C = std::input_iterator_tag > class com_iterator : public std::iterator<C, T, ptrdiff_t, T*, T*&> { typedef std::iterator<C, T, ptrdiff_t, T*, T*&> base; typedef base::iterator_category iterator_category; typedef base::value_type value_type; typedef base::reference reference; typedef base::pointer pointer; public: com_iterator() : past_end(true) { // Construct a singular iterator } com_iterator(const com_iterator &rhs) { // Copy constructor past_end = rhs.past_end; if (!past_end) { // Do I use Clone or add ref? _copy(rhs.enumerator, iterator_category()); // Increment the ref count on the underlying enumerator... // enumerator = rhs.enumerator; // ...and the current value current = rhs.current; } } explicit com_iterator(E *e):past_end(false) { // Constructs from an enumerator _copy(e, iterator_category()); // enumerator = e; acquire(); } com_iterator& operator = (const com_iterator& rhs) { // Assignment operator if (*this == rhs) return *this; past_end = rhs.past_end; if (!past_end) { _copy(rhs.enumerator, iterator_category()); // ...and the current value current = rhs.current; } return *this; } ~com_iterator() { // Destructor } bool operator == (const com_iterator& rhs) const { return _equal(rhs, iterator_category()); } inline bool operator != (const com_iterator& rhs) const { // Invokes operator == return !(*this == rhs); } reference operator * () const { // Return designated value // Requires IEnumXxx::Next() assert(!past_end); return reference(CAdapt< CComPtr<T> >(current)); //return reference(current); } pointer operator -> () const { // Return designated value // Requires IEnumXxx::Next() return pointer(current); } com_iterator& operator ++ () { // Pre-increment acquire(); return *this; } com_iterator operator ++ (int) { // Post-increment com_iterator result(*this); acquire(); return result; } private: void _copy(const CComPtr<E> &e, std::input_iterator_tag) { enumerator = e; } void _copy(const CComPtr<E> &e, std::forward_iterator_tag) { enumerator = 0; // Release if necessary e->Clone(&enumerator); } bool _equal(const com_iterator& rhs, std::input_iterator_tag) const { // Two com_iterators compare equal when they're // past-the-end iterators. if (past_end && rhs.past_end) return true; if (past_end || rhs.past_end) return false; if (enumerator != rhs.enumerator) return false; return current == rhs.current; } bool _equal(const com_iterator& rhs, std::forward_iterator_tag) const { // Two com_iterators compare equal when they're // past-the-end iterators. if (past_end && rhs.past_end) return true; if (past_end || rhs.past_end) return false; return current == rhs.current; } void acquire() { // Reads the iterator value current = 0; // Release current value HRESULT hr = enumerator->Next(1, ¤t, 0); if (FAILED(hr) || hr == S_FALSE) { past_end = true; } } private: CComPtr<E> enumerator; // Underlying enumerator CComPtr<T> current; // Current value; bool past_end; // Maintains past-the-end status }; #endif // INCLUDED_COM_ITERATOR
Jan 28 2004