-
Notifications
You must be signed in to change notification settings - Fork 802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Iter to stl #1751 redux #1753
base: stable
Are you sure you want to change the base?
Iter to stl #1751 redux #1753
Conversation
80f4e10
to
ca23e5d
Compare
With the operator-> able to access a GncTreeData, there's no need for a second accessor for the GtkIter. |
Unit tests? Or do you think this is boilerplate pass-through that doesn't need them? |
@christopherlam I need to study up on iterator class creation. This looks very strange but I imagine it's because of the way std::forward_iterator is implemented. |
here you go. |
af4f553
to
6d55d3f
Compare
|
94853c2
to
90cfc55
Compare
f366bb8
to
3cce5f1
Compare
30c5803
to
5a38017
Compare
latest iteration uses template type deduction and std::string overloading in set_column. |
5a38017
to
3836d63
Compare
The only remaining concern is that gtk4 will deprecate some of these getters/setters however I think the replacement should be straightforward |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By using separate iter->set_column
, does that mean multiple row-changed
signals. It may not matter here but maybe some thing to watch out for if so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably does, and setting a single column at a time instead of batching them into a single gtk_list_store_set
will incur a bunch of overhead just like doing a bunch of xaccTransFoo without wrapping them in xaccTransBeginEdit/xaccTransCommitEdit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably does, and setting a single column at a time instead of batching them into a single
gtk_list_store_set
will incur a bunch of overhead just like doing a bunch of xaccTransFoo without wrapping them in xaccTransBeginEdit/xaccTransCommitEdit.
It's just a convenience interface. More (IMHO) elegant than gtk_list_store_set
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is interesting what you have done and left a couple of comments.
Obviously John is in a better position to comment on the c++ code, one day I may try and get into it.
Thx for comments, a few from me
|
You can't use a vector--or any other monotype container--because the columns contain different types. It doesn't make it read-only, you can as easily write it back as read it out. You can use a struct/class if you think it will make for a cleaner interface to access the internals. Regardless, construction/destruction of the contained object whether it's a class or a tuple is not part of the container's job.
That makes it seem that you don't understand STL iterators. The whole point is to provide a consistent way to access the contained objects similar to a pointer into a basic array. Your attempt to combine iterator access with access to the object's internals breaks the model and is the source of your trouble. I'm a bit pressed for time right now, more later. |
So for the lightweight GncTreeData, You can use a That lightweight version of GncTreeData is nicely universal, meaning that you don't need to specialize it for different GtkListModels, but it offers no C++ API to the GtkListModel. You can't call The heavyweight version of GncTreeData will get very heavy and very complicated. |
I'm very aware that a GtkListModel or GtkTreeModel describes a 2D spreadsheet, whereby columns may be of different types (bool/int/void*). (And a GtkTreeModel has subrows cf GtkListModel doesn't). So a Are you describing a GtkListContainer whose ctor will pull out all of the data into the This branch in itself uses the same iterator as the underlying gtk one, and the GncTreeIter's getters/setters simply call the gtk ones. If we use the vector then we'd need to find the iter's path, find the right GtkPath in the GtkListModel, then get/set in the GtkListModel. This branch IMHO presents an already lightweight version. Nothing needs to be added to support other GtkListModels. |
You mean
Absolutely not. In a heavyweight design the GncTreeData's The benefit is a representation that fits nicely into C++ and exposes the members to the compiler for type assurance. I don't think that the time required to develop it nor the runtime cost is worth that benefit, which is why I laid out the lightweight design where
Completely missing the point: The GncTreeIter shouldn't have getters or setters. That's not an iterator's job. The thing that's the iterator's |
Note that that's true for standard algorithms that take begin and end parameters but not true for range-based for: That does require it to be a class. |
In this case I completely don't get it... It would be best revert completely the gnc-tree-container.hpp and retain the previous gtk iterators. This branch restores the GncTreeContainer deleted in bd2d318, renamed as GncListModelContainer. |
b68f072
to
ecdc05e
Compare
ecdc05e
to
00218b4
Compare
Instead of us describing the characteristics of the class, how about we decide the usage pattern of such class instead? So far my approach had been: auto container = GncListModelContainer (GTK_LIST_STORE (model));
for (auto iter : container)
{
auto acc = iter.get_column<Account*> (COL_ACCOUNT);
iter.set_column_string (COL_ACCOUNT_NAME, xaccAccountGetName (acc));
} |
Consider
What do you think that will print? |
No contest:
|
Good. How about
What would you put into |
Easy: *iter This is exactly the approach originally GncTreeIter iter = container.begin();
if (iter != container.end())
{
GncTreeData data = *iter:
std::string s = data.get_string(COL_NAME);
} |
And that is correct. GncTreeData has the It's not a great interface design though: You have to go find the declaration of the underlying GtkModel to discover COL_NAME and that it stores a string. There's also the potential efficiency issue that @Bob-IT brought up about calling gtk_tree_model_get/set once per column instead of just one with all the columns you need. On the other hand it does consolidate and hide all of fluff with passing output parameters and cleaning up the result. Which returns us to the original question, which now that I understand better what's going on I can phrase more coherently: Having to keep copies of model at every level (container needs it to generate iterators, GncTreeIter needs it to call gtk_tree_model_iter_next and friends, and GncTreeData needs it to get and set data) is ugly, as is having to keep two copies of the GtkTreeIter, worsened by having to keep those two in sync. The GncTreeIter/GncTreeData duplication can be resolved by having GncTreeIter delegate operator++ and operator== to GncTreeData, e.g. (just a sketch, much detail left out) class GncTreeData
{
GtkTreeModel *m_model;
GtkTreeIter m_iter;
public:
GncTreeData(GtkTreeModel* model, bool is_end) : m_model{model}, m_iter{}
{
if (!is_end)
gnc_tree_model_get_iter_first(m_model, &m_iter);
}
bool iter_equal(const GncTreeData& other);
bool next_iter()
{
if (m_iter.stamp)
return gtk_tree_model_iter_next(m_model, &m_iter);
return false;
}
std::pair<GtkTreeModel*, GtkTreeIter*> get_iter()
{
// parameters needed for calling arbitrary GtkTreeModel functions
return {m_model, &m_iter};
}
};
class GtkTreeIter
{
GncTreeData m_data;
bool m_end{false};
protected:
bool is_end()
{
return m_end;
}
const GncTreeData& get_model() const
{
return m_model;
}
public:
GtkTreeIter(GtkTreeModel* model, bool end = false) : m_data{model, end}, m_end{end} {}
operator==(const GncTreeIter& other)
{
if (m_end && other.is_end())
return true;
if (m_end || other.is_end())
return false;
return m_data.iter_equal(other.get_model());
}
operator++()
{
m_end = !m_model.next_iter();
}
};
class GncTreeModelContainer
{
GtkTreeModel* m_model;
public:
GncTreeModelContainer(GtkTreeModel*) : m_model{model} {}
GncTreeIter begin()
{
return GncTreeIter(m_model);
}
GncTreeIter end()
{
return GncTreeIter(m_model, true);
}
}; BTW, I haven't yet looked at your latest push, I'm just trying to show a minimal design. If you've already worked this out I'll find out tomorrow. |
This would mean that the data struct now has next() duties which IMHO belongs to the iter struct. |
Update 2: if the main concern is the |
You mean
But that doesn't confer any access to GncTreeModelContainer's private members, it just changes the namespace of the type from |
f43674f
to
ec20b34
Compare
I'll leave this parked for now. I'm not sure how to make the iter more lightweight than it is. The container should ideally be capable of all the capabilities demonstrated in the test suite. |
- defines GncListModelContainer and several methods to iterate the list store, and appropriate setters/getters. Compatible with std::find_if etc. - GncListModelContainer::append() creates a new GncTreeIter and returns it. - GncListModelContainer::size() returns the number of rows. May be O(N). To test rows==0 use empty() instead. - GncTreeData::set_column sets data column in model - GncTreeData::set_column has a std::string function overload to access its c_str()
This reverts commit ec20b34.
This reverts commit 96c7637.
ec20b34
to
c7f19c9
Compare
Initially discussed in #1745, attempted further work in #1751 now rescinded. Main branch reverted with bd2d318.
More iter changes. This now works with 80f4e10 merged in.
std::find_if
and friends, theGncTreeIter
seems to need iterator traits inherited fromstd::iterator
. See the class definitionGncTreeContainer
adds anappend()
methods which adds a newGtkTreeIter
and returns itsGncTreeIter
.GncTreeIter
adds aget_gtk_tree_iter
to return theGtkTreeIter
to be useful whenfind_if
returns theGncTreeIter
. This creates another method to access the GtkTreeIter; one from the GncTreeIter and another from GncTreeData. The benefit is this allows:instead of the uglier
std::find_if
use inimport-match-picker.cpp
.