Thread overview
COMSTL collection_sequence and enumerator_sequence have no const_iterator type
Feb 27, 2007
Gabor Fischer
Feb 27, 2007
Matthew Wilson
Feb 27, 2007
Matthew Wilson
Feb 27, 2007
Gabor Fischer
Mar 27, 2007
Matthew Wilson
Apr 07, 2007
Matthew Wilson
Apr 07, 2007
Gabor Fischer
Apr 13, 2007
Matthew Wilson
Apr 14, 2007
Gabor Fischer
Apr 15, 2007
Matthew Wilson
February 27, 2007
Hello!


The enumerator_sequence and collection_sequence of COMSTL have no const_iterator type. This breaks some generic code I work with.

IMO, every sequence and sequence adaptor should have a const_iterator typedef.




So Long...

Gabor

February 27, 2007
> The enumerator_sequence and collection_sequence of COMSTL have no const_iterator type. This breaks some generic code I work with.
> 
> IMO, every sequence and sequence adaptor should have a const_iterator typedef.

I largely agree. That it's not is simply omission, because of the peculiar relationship of the mutating natures between the iterator and begin()/end() methods.

This (the attached file) will be in the next release.

Cheers

Matthew
February 27, 2007
Hmm. Didn't attach ...

"Matthew Wilson" <no-one@nowhere.no.com> wrote in message news:es0q2l$22np$1@digitalmars.com...
> > The enumerator_sequence and collection_sequence of COMSTL have no const_iterator type. This breaks some generic code I work with.
> >
> > IMO, every sequence and sequence adaptor should have a const_iterator typedef.
>
> I largely agree. That it's not is simply omission, because of the peculiar
relationship of the mutating natures between the iterator and begin()/end()
methods.
>
> This (the attached file) will be in the next release.
>
> Cheers
>
> Matthew



February 27, 2007
Hello Matthew!


> This (the attached file) will be in the next release.

Fast work. :-) Thanks.

And while I'm at it, here are some thoughts on these two classes:
The collection_sequence class uses the enumerator interface of the
collection, which is basically a shortcut for calling get__NewEnum and
then using enumerator_sequence. IMHO it would be more convenient if
collection_sequence would use get_Item (or IDispatch with DISPID_VALUE)
for two reasons:

First, the returned types of the enumerator interface and the collection interface can be different. On automation compatible interfaces, the enumerator interface is alway IEnumVARIANT (because it is the only enum interface that is understood by scripting clients), which alwyas returns VARIANTs, whereas the collection interface itself can return any automation compatible type. Often, it is more straightforward in client code to work with the type returned by get_Item instead of having to extract it from a VARIANT first.

Second, and more important, with get_Item one has random access. So the iterators of collection_sequence could be random access iterators, and the collection_sequence itself could have an operator[] and a method at().

I also realized that you made the copy constructors and copy assignment operators private, thereby prohibiting copying. Why? The footprints of the classes are small, so copying should be cheap (and the implementation straightforward), so why preventing it?

Well, these were my thoughts on these classes for today. Anyhow, they are really useful in the project I am working on. :-)




So Long...

Gabor

March 27, 2007
[Still to work on the answers to the rest of this. However ...]

"Gabor Fischer" <Gabor.Fischer@systecs.com> wrote in message news:ABdRMg1pQNB@systecs.com...
> Hello Matthew!

<snip other stuff I've yet to reply to>

> I also realized that you made the copy constructors and copy assignment operators private, thereby prohibiting copying. Why? The footprints of the classes are small, so copying should be cheap (and the implementation straightforward), so why preventing it?

I'm copy editing XSTLv1 (http://extendedstl.com/) right at the minute, and found the bit of text I wanted to quote (even though it's bad taste to quote oneself <g>) from Chapter 19, section 19.3.2:

"

However, the copy constructor and copy assignment operator have been proscribed. Why? The reason is that readdir_sequence, just like glob_sequence (and pretty much any other file system enumeration API), provides only a snapshot of the system at a given point in time. Disallowing copy semantics prevents the user from easily forgetting this fact.



Tip: Consider proscribing operations from your types for conveying information on appropriate use, as well as for classic robustness and correctness reasons, particularly for collection types that provide snapshots of their underlying collections.


"

That's why enumerator_sequence *must* not provide these operations. I guess I made collection_sequence the same for no better reason than consistency, but if I think about it, I can certainly argue that the same reasoning *should* apply to collection_sequence (although volatile collections are _much_ more rare than non-repeatable enumerators.)

HTH

Cheers

Matthew

P.S. You might have to wait another month (!) for the rest of your answer, but I will get to it, I promise! :-)



April 07, 2007
Gabor

I'm just working through the copy-edits of chapter 30 "Adapting COM Collections" and it addresses lots of the issues you've raised in this regard.

When you posted this in Feb I thought I'd just echo the wisdom (assuming there is any) in that chapter back to you in response, but I've been having more ideas since, and the re-read today's cemented them a little more.

If you're still interested in this subject, I'm willing to pursue it along with you. I think the best way to start would be for you to post some sketches of the more direct functionality you'd _like_ to have. If that tallies with the ideas I've had, we can probably pursue them to an actual implementation.

Cheers

Matthew

P.S. btw, have you checked out the collection class in VOLE (http://vole.sf.net/). It's implemented in terms of comstl::collection_sequence, and provides Count and Item properties direct to C++ code.


"Gabor Fischer" <Gabor.Fischer@systecs.com> wrote in message news:ABdRMg1pQNB@systecs.com...
> Hello Matthew!
>
>
> > This (the attached file) will be in the next release.
>
> Fast work. :-) Thanks.
>
> And while I'm at it, here are some thoughts on these two classes:
> The collection_sequence class uses the enumerator interface of the
> collection, which is basically a shortcut for calling get__NewEnum and
> then using enumerator_sequence. IMHO it would be more convenient if
> collection_sequence would use get_Item (or IDispatch with DISPID_VALUE)
> for two reasons:
>
> First, the returned types of the enumerator interface and the collection interface can be different. On automation compatible interfaces, the enumerator interface is alway IEnumVARIANT (because it is the only enum interface that is understood by scripting clients), which alwyas returns VARIANTs, whereas the collection interface itself can return any automation compatible type. Often, it is more straightforward in client code to work with the type returned by get_Item instead of having to extract it from a VARIANT first.
>
> Second, and more important, with get_Item one has random access. So the iterators of collection_sequence could be random access iterators, and the collection_sequence itself could have an operator[] and a method at().
>
> I also realized that you made the copy constructors and copy assignment operators private, thereby prohibiting copying. Why? The footprints of the classes are small, so copying should be cheap (and the implementation straightforward), so why preventing it?
>
> Well, these were my thoughts on these classes for today. Anyhow, they are really useful in the project I am working on. :-)
>
>
>
>
> So Long...
>
> Gabor
>


April 07, 2007
Hello Matthew!

> If you're still interested in this subject, I'm willing to pursue it along with you. I think the best way to start would be for you to post some sketches of the more direct functionality you'd _like_ to have. If that tallies with the ideas I've had, we can probably pursue them to an actual implementation.

I am most definitely interested. :-) I haven't looked at VOLE yet, I will as soon as my project schedule allows it.

Ok, let's start with a simple collection interface in IDL:


[
    object,
    uuid(E55F8DAF-F559-47d3-A7A4-666956E5400C),
    dual,
    helpstring("String Collection"),
    pointer_default(unique)
]
interface IStringCollection : IDispatch
{
    [propget, id(1), helpstring("Number of Strings in Collection")]
    HRESULT Count([out, retval] long* pCount);

    [propget, id(DISPID_VALUE), helpstring("String at Index")]
    HRESULT Item([in] long n, [out, retval] BSTR* pbstrString);

    [propget, id(DISPID_NEWENUM), helpstring("Enumerator")]
    HRESULT _NewEnum([out, retval] IUnknown** ppEnum);
};


The enumerator interface of the collection is IEnumVARIANT, because we want to make it accessible to scripting clients.

Now we want to access such a collection from C++ client code. There are two possibilities: We could use the enumerator interface with comstl::enumerator_sequence. That would look somehow like this:

typedef comstl::enumerator_sequence
<
    IEnumVARIANT,
    _variant_t,
    comstl::VARIANT_policy> bstr_enum_sequence;

...

bstr_enum_sequence Strings(IEnumVARIANTPtr(spCol->Get_NewEnum()), true);
std::for_each(String.begin(), Strings.end(), process_variant_string);

The problem with that is, I have to deal with VARIANTs, when I want to process strings. It would be much more convenient if I could use the collection interface directly, whith a collection_sequence that could be used the following way:

typedef comstl::collection_sequence
<
    IStringCollection,
    _bstr_t,
    comstl::BSTR_policy> bstr_sequence;

...

bstr_sequence Strings(spCol, true);
std::for_each(Strings.begin(), Strings.end(), process_string);

That version of collection_sequence would work with get_Item (or access the element through IDispatch, that could be specifies by a policy class). IMHO a much more straightforward approach. And we could have a method at() and an operator[], so we could write

_bstr_t s0 = Strings.at(0);
_bstr_t s1 = Strings[1];

Of course, the index would be 0-based, not 1-based as it is in some implementations of COM collections (the ATL implementation, for instance). The base index should be a template parameter of com_collection, defaulted to 1.

But there is more: Collection interfaces can have more methods than in the interface outlined above, especially they can have methods for adding, removing and setting elements. So now we define a second interface in IDL:


[
    object,
    uuid(B6E9293B-0A2F-4aad-81E1-E957EB4FEE3F),
    dual,
    helpstring("Mutable String Collection"),
    pointer_default(unique)
]
interface IMutableStringCollection : IDispatch
{
    [propget, id(1), helpstring("Number of Strings in Collection")]
    HRESULT Count([out, retval] long* pCount);

    [propget, id(DISPID_VALUE), helpstring("String at Index")]
    HRESULT Item([in] long n, [out, retval] BSTR* pbstrString);

    [propput, id(DISPID_VALUE), helpstring("String at Index")]
    HRESULT Item([in] long n, [in] BSTR bstrString);

    [id(2), helpstring ("Insert String")]
    HRESULT Insert([in] long n, [in] BSTR bstrString);

    [id(3), helpstring ("Remove String")]
    HRESULT Remove([in] long n);

    [propget, id(DISPID_NEWENUM), helpstring("Enumerator")]
    HRESULT _NewEnum([out, retval] IUnknown** ppEnum);
};

Now, with a collection_sequence based on this interface and supporting such features, we could write code like

Strings[0] = "Foo";
Strings.push_back("Bar");

or even

std::sort(Strings.begin(), Strings.end(), lexicographical_compare_bstr);

Pretty cool, I'd say. :-)

It would also be useful to be able to specify some sort of copy policy class as a template parameter for collection_sequence and enumerator_sequence, which would transform the COM types to more useful types on the fly upon access (you know the copy policy classes of ATL, which are used for implementing collection interfaces?), thereby relieving the client code from having to deal with COM types at all.

Thinking further about this, maybe that idea can be generalised further to some sort of "transforming sequence"? I mean a sequence template that would offer a transformed view of another sequence. I suppose the iterators could be implemented with the transform_iterator of boost (or some similar class)?

Well that's it for today :-)




So Long...

Gabor

April 13, 2007
Hi Gabor.

I'm leaving this one marked "unread" so I remember to come back to the debate with you. However, I don't think there's any point in our having that debate until you've spent some time with VOLE, as I suspect that it covers most of the things you've mentioned here.

The main thing I can think of that it doesn't do that a custom "collection_sequence_ex" class template might is to save a couple of cycles in passing a BSTR in the server to a BSTR in the client code, rather than sticking it in a VARIANT first. However, since you mention the desirability of sticking such things in "C++"-like types, i.e. std::string, which VOLE does, then I suspect there's going to be no detectable difference between the two.

Other than that, the only thing that you discuss that VOLE does not provide is the ability to sort. But, to be honest, I don't see how the work involved to try and write a generic solution to this is ever going to be worth the effort, even if it is achievable, given that many/most collections do not provide the requisite facilities and/or have the requisite (structural) conformance. Compared with the simplicity of using an intermediate standard container in the client code, the slight additional costs would be likely worth paying.

Anyway, like I said. I've yet to read it and think it through more deeply, but I'd like to potentially save myself the effort by your checking out VOLE first. (Lazy me! <g>)

Cheers

Matthew


"Gabor Fischer" <Gabor.Fischer@systecs.com> wrote in message news:AEOhq6S$QNB@systecs.com...
> Hello Matthew!
>
> > If you're still interested in this subject, I'm willing to pursue it
along
> > with you. I think the best way to start would be for you to post some sketches of the more direct functionality you'd _like_ to have. If that tallies with the ideas I've had, we can probably pursue them to an
actual
> > implementation.
>
> I am most definitely interested. :-) I haven't looked at VOLE yet, I will as soon as my project schedule allows it.
>
> Ok, let's start with a simple collection interface in IDL:
>
>
> [
>     object,
>     uuid(E55F8DAF-F559-47d3-A7A4-666956E5400C),
>     dual,
>     helpstring("String Collection"),
>     pointer_default(unique)
> ]
> interface IStringCollection : IDispatch
> {
>     [propget, id(1), helpstring("Number of Strings in Collection")]
>     HRESULT Count([out, retval] long* pCount);
>
>     [propget, id(DISPID_VALUE), helpstring("String at Index")]
>     HRESULT Item([in] long n, [out, retval] BSTR* pbstrString);
>
>     [propget, id(DISPID_NEWENUM), helpstring("Enumerator")]
>     HRESULT _NewEnum([out, retval] IUnknown** ppEnum);
> };
>
>
> The enumerator interface of the collection is IEnumVARIANT, because we want to make it accessible to scripting clients.
>
> Now we want to access such a collection from C++ client code. There are two possibilities: We could use the enumerator interface with comstl::enumerator_sequence. That would look somehow like this:
>
> typedef comstl::enumerator_sequence
> <
>     IEnumVARIANT,
>     _variant_t,
>     comstl::VARIANT_policy> bstr_enum_sequence;
>
> ...
>
> bstr_enum_sequence Strings(IEnumVARIANTPtr(spCol->Get_NewEnum()), true);
> std::for_each(String.begin(), Strings.end(), process_variant_string);
>
> The problem with that is, I have to deal with VARIANTs, when I want to process strings. It would be much more convenient if I could use the collection interface directly, whith a collection_sequence that could be used the following way:
>
> typedef comstl::collection_sequence
> <
>     IStringCollection,
>     _bstr_t,
>     comstl::BSTR_policy> bstr_sequence;
>
> ...
>
> bstr_sequence Strings(spCol, true);
> std::for_each(Strings.begin(), Strings.end(), process_string);
>
> That version of collection_sequence would work with get_Item (or access the element through IDispatch, that could be specifies by a policy class). IMHO a much more straightforward approach. And we could have a method at() and an operator[], so we could write
>
> _bstr_t s0 = Strings.at(0);
> _bstr_t s1 = Strings[1];
>
> Of course, the index would be 0-based, not 1-based as it is in some implementations of COM collections (the ATL implementation, for instance). The base index should be a template parameter of com_collection, defaulted to 1.
>
> But there is more: Collection interfaces can have more methods than in the interface outlined above, especially they can have methods for adding, removing and setting elements. So now we define a second interface in IDL:
>
>
> [
>     object,
>     uuid(B6E9293B-0A2F-4aad-81E1-E957EB4FEE3F),
>     dual,
>     helpstring("Mutable String Collection"),
>     pointer_default(unique)
> ]
> interface IMutableStringCollection : IDispatch
> {
>     [propget, id(1), helpstring("Number of Strings in Collection")]
>     HRESULT Count([out, retval] long* pCount);
>
>     [propget, id(DISPID_VALUE), helpstring("String at Index")]
>     HRESULT Item([in] long n, [out, retval] BSTR* pbstrString);
>
>     [propput, id(DISPID_VALUE), helpstring("String at Index")]
>     HRESULT Item([in] long n, [in] BSTR bstrString);
>
>     [id(2), helpstring ("Insert String")]
>     HRESULT Insert([in] long n, [in] BSTR bstrString);
>
>     [id(3), helpstring ("Remove String")]
>     HRESULT Remove([in] long n);
>
>     [propget, id(DISPID_NEWENUM), helpstring("Enumerator")]
>     HRESULT _NewEnum([out, retval] IUnknown** ppEnum);
> };
>
> Now, with a collection_sequence based on this interface and supporting such features, we could write code like
>
> Strings[0] = "Foo";
> Strings.push_back("Bar");
>
> or even
>
> std::sort(Strings.begin(), Strings.end(), lexicographical_compare_bstr);
>
> Pretty cool, I'd say. :-)
>
> It would also be useful to be able to specify some sort of copy policy class as a template parameter for collection_sequence and enumerator_sequence, which would transform the COM types to more useful types on the fly upon access (you know the copy policy classes of ATL, which are used for implementing collection interfaces?), thereby relieving the client code from having to deal with COM types at all.
>
> Thinking further about this, maybe that idea can be generalised further to some sort of "transforming sequence"? I mean a sequence template that would offer a transformed view of another sequence. I suppose the iterators could be implemented with the transform_iterator of boost (or some similar class)?
>
> Well that's it for today :-)
>
>
>
>
> So Long...
>
> Gabor
>


April 14, 2007
Hi Metthew!


> I'm leaving this one marked "unread" so I remember to come back to the debate with you. However, I don't think there's any point in our having that debate until you've spent some time with VOLE, as I suspect that it covers most of the things you've mentioned here.

Ok. I think I will have time to take a look at VOLE sometime during May.





So Long...

Gabor

April 15, 2007
"Gabor Fischer" <Gabor.Fischer@systecs.com> wrote in message news:AEpjrB4$QNB@systecs.com...
> Hi Metthew!
>
>
> > I'm leaving this one marked "unread" so I remember to come back to the debate with you. However, I don't think there's any point in our having
that
> > debate until you've spent some time with VOLE, as I suspect that it
covers
> > most of the things you've mentioned here.
>
> Ok. I think I will have time to take a look at VOLE sometime during May.

He he. No worries. Sounds like you have the same time poverty as me at the moment. :-)

May'll be fine.

Cheers

Matt