Discussion:
Conditional lists/filters
yrs90
2005-05-19 04:29:51 UTC
Permalink
I am at the point where I need to the ability to filter a list. I considered
overriding iterators, but it looks pretty complicated. I considered
avoiding the problem by simply creating new underlying data, a new string
vector, to populate the filtered list, but I can't create a rule that allows
me to connect an item selected from the filtered list back to the original
list. For example the following rule won't work:

RULE("xrcFullList.Items", make_expr<accessor>("m_FullData"))
RULE("xrcFilteredList.Items", make_expr<accessor>("m_FilteredData"))
RULE("xrcTextCtrl.Text",
make_expr<wxString>("m_FullData[xrcFilteredList.Current].Details"))

where m_FullData is map<wxString,ComplexDataType> and m_FilteredData is
vector<wxString>. (I'm trying to avoid duplicating the entire data set.)

I am considering creating a Condition property in the lwListAdapterBase. The
idea is that an item is inserted in the list only if the Condition evaluates
true.

Currently there isn't any rule syntax that would allow me to refer to the
current item such that I could evaluate a Boolean rule for each list item.
Also, I am not sure how to cause a rule to be reevaluated.

Alternatively, (but more restrictively) I could require that the Condition
be a string value referring to a member of the aggregate currently being
inserted. In that case I think I could reference the member via
ag[GetCondition()]. Also, in this case I'm not sure yet how to invoke the
aggregate member.

Any suggestions on the easiest way to add this functionality?

Thanks,
Joel




---
[This E-mail scanned for viruses by Declude Virus]



-------------------------------------------------------
This SF.Net email is sponsored by Oracle Space Sweepstakes
Want to be the first software developer in space?
Enter now for the Oracle Space Sweepstakes!
http://ads.osdn.com/?ad_id=7412&alloc_id=16344&op=click
Hajo Kirchhoff
2005-05-19 10:36:47 UTC
Permalink
Hi,
Post by yrs90
I am at the point where I need to the ability to filter a list. I considered
I have experimented this morning and I think your easiest route will be
implementing your own iterators. It looks much more complicated than it
actually is. Here is what you need to do:

First I'd, typedef your container so that it gets its own unique name.

IMPLEMENT_CONTAINER_ADAPTER(MyContainer) instantiates the entire
container adapter mechanism. A container is really nothing more than a
container iterator factory providing access to begin() and end() and ++.

Step 1:
Write your own iterator adapter. Derive it from
container_iterator_imp_base for non-const, from
const_container_iterator_imp_base for const container access.

You need to implement the following functions:

virtual (const_)container_iterator_imp_base *clone()
create a copy of self on the heap and return it
virtual (const_)accessor get() const
return an accessor pointing to the current location
virtual void inc()
advance the iterator one element
virtual void advance(size_t off)
advance the iterator 'off' elements

Example:
typedef map<wxString, obj*> MyContainer;

class MyContainerIterator:public container_iterator_imp_base
{
MyContainer::iterator m_iterator; // the real iterator
public:
MyContainerIterator(const MyContainer::iterator &i)
:m_iterator(i)
{
}
container_iterator_imp_base *clone() const
{
return new MyContainerIterator(*this);
}
accessor get() const
{
return make_accessor(*m_iterator);
}
void inc()
{
++m_iterator;
}
void advance(size_t off)
{
while (off--)
inc();
}
};

Step 2:
You have to use template specialization to override the default iterator
factory. Add

template<>
container_iterator_imp_base
*container_converter<MyContainer>::get_begin(const schema_entry *se,
const_prop_ptr member_ptr) const
{
return new MyContainerIterator(member(member_ptr).begin());
}

in front (!) of the IMPLEMENT_CONTAINER_ADAPTER statement. It needs to
go in front so the compiler sees the specialization before it
instantiates the template.

'new MyContainerIterator...' creates a new MyContainerIterator object on
the heap and returns a pointer to it.
member(member_ptr) returns a reference to the container itself.
member(member_ptr).begin() makes sense only if the container actually
has a 'begin' method. The STL containers do, but if you where using a
different container, you'd be calling a different method.

container_converter is a class defined in dataadaptercontainerimp.h. It
has four relevant functions: get_begin(), get_end(), get_const_begin()
and get_const_end(). These functions return a pointer to a
container_iterator_imp_base for non-const container and
const_container_iterator_imp_base for const container.

You need to override all these methods.

Step 3: Add filtering.
In Step 1 you defined MyContainerIterator::inc. Modify it so that it
skips elements you want filtered out. You will also have to modify the
container_converter::get_(const_)begin methods so they will return an
iterator to the first element that matches the filter criteria instead
of just the first element.
Post by yrs90
overriding iterators, but it looks pretty complicated. I considered
avoiding the problem by simply creating new underlying data, a new string
vector, to populate the filtered list, but I can't create a rule that allows
me to connect an item selected from the filtered list back to the original
RULE("xrcTextCtrl.Text",
make_expr<wxString>("m_FullData[xrcFilteredList.Current].Details"))
But you could write your own property here. Assuming that m_FullData is
part of a struct MyData, write a FullDataCurrent property with get/set
methods that reflect m_FullData[xrcFilteredList.Current]. But its a cludge.
Post by yrs90
I am considering creating a Condition property in the lwListAdapterBase. The
idea is that an item is inserted in the list only if the Condition evaluates
true.
Currently there isn't any rule syntax that would allow me to refer to the
current item such that I could evaluate a Boolean rule for each list item.
Also, I am not sure how to cause a rule to be reevaluated.
Hm, intriguing. You are suggesting a property that is itself an
expression or rule. So that I could pass an expression to the property
and it would be evaluated when needed. I'll have to keep that in mind.
This is possible and I like the idea.
Post by yrs90
Any suggestions on the easiest way to add this functionality?
Try writing your own iterators. Its not very hard to do, it just looks
this way since its still undocumented.

Regards

Hajo



-------------------------------------------------------
This SF.Net email is sponsored by Oracle Space Sweepstakes
Want to be the first software developer in space?
Enter now for the Oracle Space Sweepstakes!
http://ads.osdn.com/?ad_id=7412&alloc_id=16344&op=click
yrs90
2005-05-20 07:08:29 UTC
Permalink
Post by Hajo Kirchhoff
I have experimented this morning and I think your easiest route will be
implementing your own iterators. It looks much more complicated than it
Well that was very nice. Thank you.

As I was working on this, I noticed that there doesn't seem to be any check
that the iterator does not increment past the last element. In particular
advance() seems susceptible to overrunning the container in the basic case.
In implementing the filter feature, I need to compare against the end
element in a few other locations too, but I don't see any way to access
end() or npos or whatever from within a container_iterator_imp_base subclass
or via access to the stl container's iterator. Am I missing something?

While I was searching around looking for something within the iterator class
that would tell me if I was at the end, I stumbled across the boost
library's filtered iterator implementation.
(http://www.boost.org/libs/iterator/doc/filter_iterator.html) They appear to
address the boundary condition test by explicitly passing an end iterator.
I wonder if using it would be as simple as declaring the filtered iterator
as a litwindow container.

This actually highlights a conceptual problem I was having. I am defining a
new container (a set<MyListElementDataClass*>) to go with the customized
iterator. It seems clumsy, but I don't see any way to pass an adaptive
filter to a rule in litwindow. Once I resign myself to creating a new
container/iterator implementation for each filtered view, I find that the
independence of the iterator from the container prevents me from passing the
end value to the iterator.

I appreciate the direction thus far. The instructions were very clear. If I
can solve the 'end' problem, I think I can give this a try. Otherwise, I
could embark on an exploration of boost library (which looks fairly
elegant), but I'm sure that will be time consuming too...

Regards,
Joel



---
[This E-mail scanned for viruses by Declude Virus]



-------------------------------------------------------
This SF.Net email is sponsored by Oracle Space Sweepstakes
Want to be the first software developer in space?
Enter now for the Oracle Space Sweepstakes!
http://ads.osdn.com/?ad_id=7412&alloc_id=16344&op=click
Hajo Kirchhoff
2005-05-21 05:06:54 UTC
Permalink
Hi,
Post by yrs90
As I was working on this, I noticed that there doesn't seem to be any check
that the iterator does not increment past the last element. In particular
advance() seems susceptible to overrunning the container in the basic case.
that is correct. The current implementation follows the STL iterators
closely and like them does not have any checks. Actually I am not sure
if advance is really used at all or if it was just a failed attempt at
starting with the bookmarks implementation.
Post by yrs90
In implementing the filter feature, I need to compare against the end
element in a few other locations too, but I don't see any way to access
end() or npos or whatever from within a container_iterator_imp_base subclass
or via access to the stl container's iterator. Am I missing something?
No. If you need this you have to do it yourself.
Post by yrs90
While I was searching around looking for something within the
iterator class
Post by yrs90
that would tell me if I was at the end, I stumbled across the boost
library's filtered iterator implementation.
(http://www.boost.org/libs/iterator/doc/filter_iterator.html) They appear to
address the boundary condition test by explicitly passing an end iterator.
I wonder if using it would be as simple as declaring the filtered iterator
as a litwindow container.
If it follows the STL container conventions, yes. If it doesn't, you
will have to override the make_container_iterator and container_iterator
mechanism.

BTW, I can *highly* recommend the boost library. I have all but
finalized my decision to use boost with the Lit Window Library anyway.
Post by yrs90
filter to a rule in litwindow. Once I resign myself to creating a new
container/iterator implementation for each filtered view, I find that the
independence of the iterator from the container prevents me from passing the
end value to the iterator.
A way around this are the container_converter::get_begin() /
container_converter::get_const_begin methods. You have to override them
anyway and can pass a pointer to the container to your iterator. STL
iterators are fast but extremely simple. The IBM Open Class Library for
example had a much more sophisticated concept. And iterators there
always had a pointer back to the owning container.

BTW, passing end() to the iterator will not be sufficient in your case,
since end() may change over time. So you really need the pointer to the
container itself and get 'end()' everytime you need it.
Post by yrs90
I appreciate the direction thus far. The instructions were very clear. If I
can solve the 'end' problem, I think I can give this a try. Otherwise, I
could embark on an exploration of boost library (which looks fairly
elegant), but I'm sure that will be time consuming too...
If you have the time, explore it. It is well worth it. Start with
shared_ptr<>, if you are not already using it.

Best regards

Hajo



-------------------------------------------------------
This SF.Net email is sponsored by Oracle Space Sweepstakes
Want to be the first software developer in space?
Enter now for the Oracle Space Sweepstakes!
http://ads.osdn.com/?ad_id=7412&alloc_id=16344&op=click

Loading...