www.digitalmars.com         C & C++   DMDScript  

c++.stlsoft - COM Iterator

reply "Javier Estrada" <ljestrada hotmail.com> writes:
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, &current, 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
parent "Matthew" <matthew.hat stlsoft.dot.org> writes:
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 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, &current, 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