Thread: [litwindow-users] <map> as container
Status: Alpha
Brought to you by:
hajokirchhoff
From: yrs90 <yr...@ya...> - 2005-04-25 07:30:54
|
I have been pondering <map> containers again. The reason that <map> could not be used as a container was that it didn't have a way to be bookmarked like a vector, is that right? I'm not sure I understand the limitation. It seems that there are at least two possible ways of dealing with bookmarks using a <map>. If the bookmarks are refreshed when a NotifyChanged() is received, the existing implementation of indexing off the begin() iterator looks sufficient. As it stands now, when a new data element is inserted or deleted, the entire view is repopulated, so the index can be refreshed with the updated ordering. If the bookmark needs to persist in the face of insertions and deletions, the iterator itself could be used as the bookmark. <map> has the nice feature of not invalidating the iterators when an element is inserted or deleted (other than the deleted element). This is less volatile than a <vector>. Am I overlooking something? Best regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-25 08:54:05
|
yrs90 wrote: > I have been pondering <map> containers again. > > The reason that <map> could not be used as a container was that it didn't > have a way to be bookmarked like a vector, is that right? I'm not sure I > understand the limitation. It seems that there are at least two possible > ways of dealing with bookmarks using a <map>. The bookmarks referred to in the vision document do not exist yet. Also the discussion in the vision document is at a very high level of abstraction. map<> are not suitable for the high-level container concept because it does not preserve a sorting order. map<> always impose their own sorting order. This high-level container might even contain more than one 'index'. Implementation wise it could be an stl vector that maintains the custom sort order plus several other vectors, each representing a different 'index' that contain pointers to the original data. Sorting offers a subtle design problem. Is the sort order an inherent attribute of the collection? Or is the sort order just a 'view' to the collection, imposed by the user. This problem becomes clear when you think of a multi-user scenario. Suppose you had a list of addresses. My preferred sort order will be different from your sort order, so 'sort' is not an attribute of the collection. So, should sorting be part of the container or not? The current reason why map<> cannot be used with IMPLEMENT_CONTAINER_ADAPTER is much simpler. I haven't had the time to write a map container adapter. The only container adapter that exists so far is the vector container adapter. > If the bookmarks are refreshed when a NotifyChanged() is received, the > existing implementation of indexing off the begin() iterator looks > sufficient. As it stands now, when a new data element is inserted or > deleted, the entire view is repopulated, so the index can be refreshed with > the updated ordering. > > If the bookmark needs to persist in the face of insertions and deletions, > the iterator itself could be used as the bookmark. <map> has the nice > feature of not invalidating the iterators when an element is inserted or > deleted (other than the deleted element). This is less volatile than a > <vector>. Yes, true. Currently, NotifyChanged works with the entire container only. What would be needed is NotifyInserted and NotifyDeleted for containers. This area of the Lit Window Library is not implemented yet. Here is what I think needs to be done: Idea 1: Insertion / deletion is being handled by the container itself. Interested objects are being notified. NotifyInserted should notify any interested object that an insertion has occurred in a container. It should also include a 'bookmark' as to where this insertion has occurred. The bookmark should point to the object immediately behind the insertion. Bookmarks in this sense are an abstract concept of iterators. ODBC bookmarks will look entirely different than STL iterators, so 'bookmark' is a high level abstraction, much as the container adapter is a high level abstraction to a concrete container. NotifyDeleted should notify any interested object that a deletion has occurred. Perhaps the notification should also include NotifyInserting - NotifyDeleting events that are being sent just before the insertion/deletion is being made. Idea 2: Insertion / deletion is never done by the container itself, but by some control that owns the container. This would make the insertion/deletion easier, but you couldn't bind more than one control to a container. My current advice is not to rely on the library for this case. There still needs some conceptual work to be done and the container abstraction needs to be enhanced for this in order to work. If you need a map, you could use a map as an index in addition to the vector. Or write the container adapter for a map, which is not very difficult. IMPLEMENT_CONTAINER_ADAPTER will then work for map<> as well. To avoid repopulating the entire view every time a single element is inserted or removed from the view, you will have to write your own notification mechanism at this time. The problem is that you can currently access elements inside a container only sequentially. That is if you have a litwindow::container mycontainer=myaccessor.get_container(); and want to access a specific (say the 12th) element, you can only use size_t element_index=12; container::iterator i; while (element_index--) ++i; There is no [] operator yet. This would be equivalent to a 'seek' operation. What is needed is something along the lines of container::bookmark bmark; container my_container=my_accessor.get_container(); container::iterator i=my_container.seek(bmark); which would return a container iterator pointing to the element at 'bmark'. bmark would either have to be valid even after insert/delete operations or all affected bmarks would have to be invalidated. Alternatively, instead of having both, iterators and bookmarks, I could imagine combining the two classes and give iterators the properties of a bookmark. Currently iterators are bound closely to the underlying iterator algorithm, vector::iterator in this case. So they can become invalid after an insert operation. Bookmarks otoh would be a much higher-level abstraction and should be unique for every object inside the container. My current thinking is having bookmark as a unique 'address' of an element in a container. Bookmarks should remain valid for all operations. Iterators otoh will be short-lived objects used to access and iterate the container. Their sort order can be changed without changing the actual sort order of the underlying container. If you are familiar with databases, another way to think of bookmarks, containers and iterators is this: A container is a database table. It does not have a sort order - or perhaps it as a default sort order. A bookmark is just that, a bookmark. Most database engines support bookmarks for rows inside a table. A bookmark remains valid as long as the row exists, regardless of other insertion/deletion operations. An iterator is a cursor. When you 'SELECT * FROM table' you really open a cursor and iterate over the table with GetFirst() and GetNext(). Optionally cou can pass a sort order to the SELECT statement so the cursor will return the objects in a particular order. Translated to the container adapters this would mean: // get a handle to the container (table) container my_container=my_accessor.get_container(); // iterate over the container using a particular sort order container::iterator i; for (i=my_container.get_iterator("SORT BY m_name"); i.at_end()==false; ++i) // save a bookmark for an element that iterests me // return the bookmark for the current iterator as a long value // associate the bookmark with the 'ItemData' property of the current list control element some_list_ctrl->SetItemData(index, i.get_bookmark().as_long()); // seek to a specific bookmark i=my_container.get_iterator("SORT BY other_property"); i.seek(bookmark((long)some_list_ctrl->GetItemData())); Something along these lines. Best regards Hajo |
From: Hajo K. <mai...@ha...> - 2005-04-25 10:17:59
|
I forgot to add: list<> already works, so if you need a container that does not invalidate the iterators after an insert/delete operation, you could use list. Hajo |
From: yrs90 <yr...@ya...> - 2005-04-26 05:25:33
|
> Is the sort order an inherent attribute of the > collection? Or is the sort order just a 'view' to the collection, > imposed by the user.... So, should sorting be part of the container > or not? I had assumed that sort order should be part of the view rather than part of the container. Multiple views of data are not uncommon. Multiple views probably require that sort order be independent of the container. > Iterators otoh will be short-lived objects used to access and iterate > the container. Their sort order can be changed without changing the > actual sort order of the underlying container. This is an interesting perspective. I had assumed that sorting would be handled by the view itself rather than by an iterator. Is there a way to provide sorted iterators efficiently? > Bookmarks otoh would be a much higher-level abstraction and should be > unique for every object inside the container. > > My current thinking is having bookmark as a unique 'address' of an > element in a container. Bookmarks should remain valid for all operations. > ... > A bookmark is just that, a bookmark. Most database engines support > bookmarks for rows inside a table. A bookmark remains valid as long as > the row exists, regardless of other insertion/deletion operations. This sounds a lot like the iterator for a list or map. > This high-level container might even contain more than one 'index'. I don't see why it is desirable to have more than one index to a given container element. Well, that is unless you mean to have the index not only indicate the item, but also the particular sorting that was used. But that seems unnecessary since element ordering is available from the view (or through a sortable iterator). > Idea 2: > Insertion / deletion is never done by the container itself, but by some > control that owns the container. > > This would make the insertion/deletion easier, but you couldn't bind > more than one control to a container. Restricting to a single association between a control and a container is too constraining for a general framework, I think. Although, so long as (a) only one control could access a given container at any given time and (b) a container could notify all bound controls when change had occurred, then I think it would work for multiple controls bound to a single container too. > Idea 1: > Insertion / deletion is being handled by the container itself. > Interested objects are being notified. The only troublesome part of this is that it takes a bit of work for a control to gain access to its bound container. I expect the access mechanism could hidden, which would make this the more versatile approach. For one thing, a deletion from a view might be achieved by setting a container element attribute to "hidden". Best regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-28 06:21:46
|
yrs90 wrote: >>Is the sort order an inherent attribute of the >>collection? Or is the sort order just a 'view' to the collection, >>imposed by the user.... So, should sorting be part of the container >>or not? > > > I had assumed that sort order should be part of the view rather than part of > the container. Multiple views of data are not uncommon. Multiple views > probably require that sort order be independent of the container. Take the following problem: You are writing a bug tracking application and are offering the user a 'priority' enumeration: low, medium, high, critical. You also want to let the user modify this list. The sort order here is implied by the semantics of the object, not by a view. >>Iterators otoh will be short-lived objects used to access and iterate >>the container. Their sort order can be changed without changing the >>actual sort order of the underlying container. > > > This is an interesting perspective. I had assumed that sorting would be > handled by the view itself rather than by an iterator. Is there a way to > provide sorted iterators efficiently? I don't know yet, but why not. These iterators would not sort the underlying objects in the container itself, of course. Rather they would be a sorted vector of pointers to the objects. This is the same approach that ADO(.NET) uses with ADO record sets. You load a recordset into memory (-> container) and then get a cursor (-> iterator). The nice thing is you can have more than one cursor and can specify a sort order for each cursor. The lit window container abstraction will then of course be a lot higher-level than the STL. And performance will not always be as good as the STL. >>This high-level container might even contain more than one 'index'. > > > I don't see why it is desirable to have more than one index to a given > container element. Well, that is unless you mean to have the index not only > indicate the item, but also the particular sorting that was used. But that > seems unnecessary since element ordering is available from the view (or > through a sortable iterator). What exactly is a view? Imagine a container and a program that has more than one list control that is being connected to the container. How would you solve the problem that each list control can have a different sort order? > Restricting to a single association between a control and a container is too > constraining for a general framework, I think. Although, so long as (a) > only one control could access a given container at any given time and (b) a > container could notify all bound controls when change had occurred, then I > think it would work for multiple controls bound to a single container too. I agree. >>Idea 1: >>Insertion / deletion is being handled by the container itself. >>Interested objects are being notified. > > The only troublesome part of this is that it takes a bit of work for a > control to gain access to its bound container. I expect the access > mechanism could hidden, which would make this the more versatile approach. > For one thing, a deletion from a view might be achieved by setting a > container element attribute to "hidden". So you think of every control as a view to the container? Where and how do you save the view attributes? Another idea: The lit window library could introduce a 'filter' object, which is basically a view. This is an idea that pops up every now and then. A filter would take an accessor (aggregate or container), modify it in specific ways and represent this modification as an accessor itself. You could bind your control to a filter and bind the filter to a container. Filter would then essentially become a view. The control could change the filter's sort order, perhaps there really could be a filter expression so you could display only specific parts of containers. Another example of where filters where handy would be the column headers. Currently you have the 'columnheader' property. But you could filter the aggregates, remove unwanted members from the aggregate and change the names of the other members and the order through a filter. Hajo |
From: yrs90 <yr...@ya...> - 2005-04-29 06:45:53
|
> Take the following problem: You are writing a bug tracking application > and are offering the user a 'priority' enumeration: low, medium, high, > critical. You also want to let the user modify this list. > > The sort order here is implied by the semantics of the object, not by a > view. Perhaps part of what leads to the ambiguity over where to sort is that the wx controls store a copy of the data. So, for example, the wxListCtrl can sort its contents independent of the source data. Perhaps container or iterator base ordering is more an issue in a lightweight control. My initial reaction is that imposing external source-data/iterator ordering on the control may either conflict with or reinvent the control management. Perhaps there are some rules that just apply to the control (on column click, sort by column) while other rules deal with the moving of data to and from the control (on delete key, delete element from container). In your example, perhaps I don't understand the distinction you are making. The priority is an attribute of the object by which the view can sort. It might just as easily sort by another attribute like the date. Perhaps one view has a priority sorted list while another shows a histogram of bugs by priority for each day. > I don't know yet, but why not. These iterators would not sort the > underlying objects in the container itself, of course. Rather they would > be a sorted vector of pointers to the objects. This is the same approach > that ADO(.NET) uses with ADO record sets. You load a recordset into > memory (-> container) and then get a cursor (-> iterator). The nice > thing is you can have more than one cursor and can specify a sort order > for each cursor. This sounds useful. > The lit window container abstraction will then of course be a lot > higher-level than the STL. And performance will not always be as good as > the STL. Perhaps the user could decide the cost by using a property 'Sorted' that would use the costlier sorted iterator rather than the native stl iterator. > > >>This high-level container might even contain more than one 'index'. > > > > > > I don't see why it is desirable to have more than one index to a given > > container element. Well, that is unless you mean to have the index not > only > > indicate the item, but also the particular sorting that was used. But > that > > seems unnecessary since element ordering is available from the view (or > > through a sortable iterator). > > What exactly is a view? > > Imagine a container and a program that has more than one list control > that is being connected to the container. How would you solve the > problem that each list control can have a different sort order? In the case of wxListCtrl, the sort criteria can be bound to the control. Now the next question is how sort criteria can be described by a rule. I am not sure. > So you think of every control as a view to the container? I do. Am I redefining the term? > Where and how do you save the view attributes? In some cases the controls have provisions to save these attributes. When they don't, I am not sure. Will sortable iterators (which I've started to think are a good idea) help address this problem? > Another idea: The lit window library could introduce a 'filter' object, > which is basically a view. This is an idea that pops up every now and > then. A filter would take an accessor (aggregate or container), modify > it in specific ways and represent this modification as an accessor > itself. You could bind your control to a filter and bind the filter to a > container. Filter would then essentially become a view. The control > could change the filter's sort order, perhaps there really could be a > filter expression so you could display only specific parts of containers. How would selective display be achieved without using a filter object? These seem like a necessary feature. Could filter behavior be rules based too? > Another example of where filters where handy would be the column > headers. Currently you have the 'columnheader' property. But you could > filter the aggregates, remove unwanted members from the aggregate and > change the names of the other members and the order through a filter. Those filter rules would require just about as much customization as the columnheader property. On the other hand, it would make it possible to dynamically alter the number of columns that were displayed. This sounds useful. Actually I was wondering about filtering earlier too. I guess I need to implement a new iterator for my container in order to achieve filtering, right? I can't create a rule that accesses a container via function calls? Regard, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-29 14:19:10
|
Hi >> Take the following problem: You are writing a bug tracking application >> and are offering the user a 'priority' enumeration: low, medium, high, >> critical. You also want to let the user modify this list. >> >> The sort order here is implied by the semantics of the object, not by a >> view. > In your example, perhaps I don't understand the distinction you are making. > The priority is an attribute of the object by which the view can sort. It > might just as easily sort by another attribute like the date. Perhaps one > view has a priority sorted list while another shows a histogram of bugs by > priority for each day. But exactly how do you sort the view? Where is the sort order defined? Clicking on a column header of a list control should sort the bugs by ascending or descending priority. But all you have is a container of strings "low, medium, high, critical" and the alphabetical sort order is useless. It would be 'critical', 'high', 'low', 'medium'. So where would a generic list control get the true sort order from? I think this is a case where the sort order is a fixed property of the container, not of a view of the container. That is why I make a distinction between sort order imposed by a container and a sort order that depends on a view and can be different from user to user. I think we need both. >> So you think of every control as a view to the container? > > > I do. Am I redefining the term? No. I just wanted to make sure. > >> Where and how do you save the view attributes? > > > > In some cases the controls have provisions to save these attributes. When > they don't, I am not sure. Will sortable iterators (which I've started to > think are a good idea) help address this problem? They might, if they can encapsulate all the attributes that the controls need. >> Another idea: The lit window library could introduce a 'filter' object, >> which is basically a view. > > How would selective display be achieved without using a filter object? These > seem like a necessary feature. Could filter behavior be rules based too? Sure. Filter behaviour would just be another data type and could be set like every other object, too. > Actually I was wondering about filtering earlier too. I guess I need to > implement a new iterator for my container in order to achieve filtering, > right? I can't create a rule that accesses a container via function calls? Not at the moment. No. Creating your own iterator is the way to do this. Hajo |
From: Hajo K. <mai...@ha...> - 2005-04-25 10:51:53
|
Hi all, I am about to commit changes that allow a map<> to be used with container adapters. In order to use it you must first use a typedef typedef std::map<std::string, int> the_map; then define an aggregate adapter for the pair<string, int> // note: the LWL_.... macros will replace the BEGIN_.... macros // LWL_ stands for Lit Window Library. I've lifted this scheme // from the boost library. LWL_BEGIN_AGGREGATE_NO_COPY(the_map::value_type) PROP(first) PROP(second) LWL_END_AGGREGATE() and finally use IMPLEMENT_ADAPTER_CONTAINER to make the adapter known to the system. IMPLEMENT_ADAPTER_CONTAINER(the_map) Have a look at unittests\containertests.cpp to see how it is used. Expect commit within 30 minutes. Regards Hajo |
From: yrs90 <yr...@ya...> - 2005-04-25 11:55:04
|
Many thanks! My attempt has been stymied the proper form of the <pair> declaration, but I see you've addressed that. I'll take a look. Best regards, Joel -----Original Message----- Hi all, I am about to commit changes that allow a map<> to be used with container adapters. In order to use it you must first use a typedef typedef std::map<std::string, int> the_map; then define an aggregate adapter for the pair<string, int> // note: the LWL_.... macros will replace the BEGIN_.... macros // LWL_ stands for Lit Window Library. I've lifted this scheme // from the boost library. LWL_BEGIN_AGGREGATE_NO_COPY(the_map::value_type) PROP(first) PROP(second) LWL_END_AGGREGATE() and finally use IMPLEMENT_ADAPTER_CONTAINER to make the adapter known to the system. IMPLEMENT_ADAPTER_CONTAINER(the_map) Have a look at unittests\containertests.cpp to see how it is used. Expect commit within 30 minutes. Regards Hajo --- [This E-mail scanned for viruses by Declude Virus] |
From: yrs90 <yr...@ya...> - 2005-04-26 06:03:01
|
> I am about to commit changes that allow a map<> to be used with > container adapters. I tried the updated code you submitted. (In fact I tried a messier version of the same thing at one point yesterday using <pair> rather than value_type.) However even using your updates, I get an error from RapidUI. Now I realize I might have been focusing on the container when the problem is now my rule. I have a rule which states: RULE("xrcList", make_expr<accessor>("m_MapOfItems")) When I run, I get the assertion during startup: std::runtime_error: from_accessor method not implemented The assertion appears to be correct. The from_accessor looks like it is not implemented when <pair> is declared ...ADAPTER_NO_COPY. However, without NO_COPY there will be problems because of operator=. Why doesn't it want to play with the list? Regards, Joel --- [This E-mail scanned for viruses by Declude Virus] |
From: Hajo K. <mai...@ha...> - 2005-04-26 07:41:51
|
Hi, > When I run, I get the assertion during startup: > std::runtime_error: from_accessor method not implemented > > The assertion appears to be correct. The from_accessor looks like it is not > implemented when <pair> is declared ...ADAPTER_NO_COPY. However, without > NO_COPY there will be problems because of operator=. Why doesn't it want to > play with the list? that is unfortunate. Hm. The problem here is that indeed the pair<> does not have a copy constructor or = operator. But the list code wants to access map elements by value. from_accessor does the same thing as from_string: it sets the value of an object pointed to by an accessor from a different accessor, whereas from_string would read the value from a string. I will have to think about how to solve this problem. This wasn't an issue with vector<>, because the elements of the vector could be copied and thus ...ADAPTER without NO_COPY could be used. I may have to introduce a type_traits class that encapsulates assignment, comparison and other operations. That way you can specialize the type_traits template when you know how to copy an object but there is no = operator or copy constructor available. In this case it would mean writing something like template<> void type_traits<pair<string, int> >::assign(pair<string, int> &to, const pair<string, int> &from) { to.first=from.first; to.second=from.second; } The entire = and copy constructor issue is causing many problems with the data adapter design. Here is where the problem actually stems from: When you define get/set properties you have to be able to copy elements. If it were all member variables I could simply pass a pointer to the object around. But I cannot pass a pointer to the result of a get_property call. So the data adapter mechanism tries to do both: use pointers when possible and use the copy scheme when get_/set_ property methods are used. Some objects don't have a copy constructor or = operator. The first distinction then is the one between ...ADAPTER and ...ADAPTER_NO_COPY. The ...ADAPTER_NO_COPY creates a data adapter with more limited functionality. from_accessor is not defined for example, because from_accessor tries to assign an object. Hmm. Difficult. Hajo |