www.digitalmars.com         C & C++   DMDScript  

c++.stlsoft - COMSTL collection_sequence and enumerator_sequence have no const_iterator type

reply Gabor.Fischer systecs.com (Gabor Fischer) writes:
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
Feb 26 2007
parent reply Matthew Wilson <no-one nowhere.no.com> writes:
 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
Feb 27 2007
next sibling parent "Matthew Wilson" <matthew hat.stlsoft.dot.org> writes:
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

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

 Cheers

 Matthew

Feb 27 2007
prev sibling next sibling parent reply Gabor.Fischer systecs.com (Gabor Fischer) writes:
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
Feb 27 2007
next sibling parent "Matthew Wilson" <matthew hat.stlsoft.dot.org> writes:
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

[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 =

 operators private, thereby prohibiting copying. Why? The footprints of =

 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. =20 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! :-)
Mar 26 2007
prev sibling next sibling parent reply "Matthew Wilson" <matthew hat.stlsoft.dot.org> writes:
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

Apr 06 2007
parent "Matthew Wilson" <matthew hat.stlsoft.dot.org> writes:
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


 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


 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

Apr 12 2007
prev sibling parent reply Gabor.Fischer systecs.com (Gabor Fischer) writes:
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
Apr 14 2007
parent "Matthew Wilson" <matthew hat.stlsoft.dot.org> writes:
"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


 debate until you've spent some time with VOLE, as I suspect that it


 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
Apr 14 2007
prev sibling parent Gabor.Fischer systecs.com (Gabor Fischer) writes:
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
Apr 07 2007