Hello, first of all thanks for your great work! :-)

For the listbox there are two models. Typically, if you have complicated data and you want to avoid data duplication and senseless back and forward conversions, you'd pick the shared_model.
The problem is that this model still forces you to do unnecessary conversions.
If you pick a shared_model and want to handle the container yourself, the model should pass iterators to the custom callbacks.

Let's take an example random access container (deque) with various columns of floating point numbers. The first column being the index of the elements (increment from 1 to last index):

Number | Value  | Cost
======================
1      | 30.373 | 2.39
2      | 40.702 | 9.99
...

If you provide your custom sort function for this:

set_sort_compare (size_type col, std::function< bool(const std::string &, nana::any *, const std::string &, nana::any *, bool reverse)> strick_ordering)

you end up with converting floats to strings and string to floats all the time.
If instead you'd simply pass the iterators, you could access your items (objects) directly the way you want. You could also deduce the index without cost, as it is a random access container (std::distance).
Likewise, when passing a callback for the cell translation you currently pass the item to the callback. There is no way to tell the index by that. You'd have to store an extra index for the first column, in this example. If instead you'd pass an iterator, you'd have both the item and the index efficiently.

How about using iterators instead?

Hello, first of all thanks for your great work! :-) For the listbox there are two models. Typically, if you have complicated data and you want to avoid data duplication and senseless back and forward conversions, you'd pick the **shared_model**. The problem is that **this model still forces you to do unnecessary conversions**. **If you pick a shared_model and want to handle the container yourself, the model should pass iterators to the custom callbacks. ** Let's take an example random access container (deque) with various columns of floating point numbers. The first column being the index of the elements (increment from 1 to last index): ```` Number | Value | Cost ====================== 1 | 30.373 | 2.39 2 | 40.702 | 9.99 ... ```` If you provide your custom sort function for this: ```` set_sort_compare (size_type col, std::function< bool(const std::string &, nana::any *, const std::string &, nana::any *, bool reverse)> strick_ordering) ```` you end up with converting floats to strings and string to floats all the time. If instead you'd simply pass the iterators, you could access your items (objects) directly the way you want. You could also deduce the index without cost, as it is a random access container (std::distance). Likewise, when passing a callback for the cell translation you currently pass the item to the callback. There is no way to tell the index by that. You'd have to store an extra index for the first column, in this example. If instead you'd pass an iterator, you'd have both the item and the index efficiently. How about using iterators instead?
edited May 1 at 3:30 pm

Hi, thanks for your feedback! The main problem is that listbox doesn't know anything about the container and the value_type of the container. Only cell is used for transferring data between the container and listbox.

Do you have any idea about how to design the API for iterators?

Hi, thanks for your feedback! The main problem is that `listbox` doesn't know anything about the container and the value_type of the container. Only `cell` is used for transferring data between the container and `listbox`. Do you have any idea about how to design the API for iterators?

The way I see it, there should be two versions.

  • The first being the simple approach, with the listbox handling things internally.
  • The second should only be a skeleton, allowing you to "do things your own way".

Of course, you can combine the two and have std::function pointers with default values.
Implementing the model-view is overrated. These days, everybody can do that easily by simply copying the objects as std::shared_ptr into another container with different properties/orders. You only have to take care that you add/remove/modify all of them, which isn’t difficult.

A simple approach would be by having multiple std::function objects for the callbacks (this can probably be eliminated via template and constexpr). This way you can have your callback for sorting, which passes the items and you can have the – much more efficient version – which only passes you the index or iterator and the column to sort by (+ the order). If the latter function is non-zero you take that call, otherwise you take the other (when this is a template, the superfluous code will be removed).

Think of something simple like a list of patients:

First Name | Last Name | Date of Birth | Last Appointment | ...

Here, the last appointment would be a date. Internally, you’d handle this as a time_t in your object. In the listbox it is shown like you would display dates in a file requester. That is, for newer dates you also add the weekdays. This way you can easily see that the person was there last Friday, for instance.
Think of all the fuzz you’d have to go through for handling such a simple list the abstract fashion. You’d have to "parse" dates and convert them back and forth for each sorting and so on. This would be VERY inefficient.
Wouldn’t it be better if you could choose (template parameter std::function) to receive an index or an iterator instead? You could handle this MUCH more efficient yourself, knowing what your object looks like and how it works.

As for the selection and additional data, you should give the programmer the possibility to include that "payload" into the object. Normally, a listbox is a std::deque with std::shared_ptr to the objects. This is the standard/efficient way of holding bigger tables. The objects remain small (only shared_ptr) when sorting/modifying the container and you can have several views (models) by simply copying the shared pointers. If, for instance, you have a list of proxies, you can have an additional std::unordered_set hashing the raw IPs. This way you can handle duplicate entries efficiently.
If you maintain an additional – separate - container just for the payload (item selection), things become inefficient. You could add a std::function to the template parameters, which gives you a reference to the payload. That is, you have a public struct with your selection, checkbox or whatever status information and the std::function will return this struct to you if you give it the index or iterator. If the std::function is null (if constexpr or inline, so it will be removed appropriately) you could keep your separate container.
You might even consider giving the programmer the possibility to implement this functionality. In this case, you would not have a std::function which returns the struct, but instead functions for each status item - "bool isSelected( std::const_iterator ) const" etc. This way, one could choose to implement the selection more efficiently (less memory usage) in a custom list:
Selected:
5-12
15-15
19-233

In short, if you give the programmer the possibility to overwrite/disable the default behavior by passing custom functions (which receive the "raw" information = index/iterator), things can be handled A LOT more efficient (and easier, as you need less code when not treating your own containers like abstract objects).

The way I see it, there should be two versions. - The first being the simple approach, with the listbox handling things internally. - The second should only be a skeleton, allowing you to "do things your own way". Of course, you can combine the two and have std::function pointers with default values. Implementing the model-view is overrated. These days, everybody can do that easily by simply copying the objects as std::shared_ptr into another container with different properties/orders. You only have to take care that you add/remove/modify all of them, which isn’t difficult. A simple approach would be by having multiple std::function objects for the callbacks (this can probably be eliminated via template and constexpr). This way you can have your callback for sorting, which passes the items and you can have the – much more efficient version – which only passes you the index or iterator and the column to sort by (+ the order). If the latter function is non-zero you take that call, otherwise you take the other (when this is a template, the superfluous code will be removed). Think of something simple like a list of patients: First Name | Last Name | Date of Birth | Last Appointment | ... Here, the last appointment would be a date. Internally, you’d handle this as a time_t in your object. In the listbox it is shown like you would display dates in a file requester. That is, for newer dates you also add the weekdays. This way you can easily see that the person was there last Friday, for instance. Think of all the fuzz you’d have to go through for handling such a simple list the abstract fashion. You’d have to "parse" dates and convert them back and forth for each sorting and so on. This would be VERY inefficient. Wouldn’t it be better if you could choose (template parameter std::function) to receive an index or an iterator instead? You could handle this MUCH more efficient yourself, knowing what your object looks like and how it works. As for the selection and additional data, you should give the programmer the possibility to include that "payload" into the object. Normally, a listbox is a std::deque with std::shared_ptr to the objects. This is the standard/efficient way of holding bigger tables. The objects remain small (only shared_ptr) when sorting/modifying the container and you can have several views (models) by simply copying the shared pointers. If, for instance, you have a list of proxies, you can have an additional std::unordered_set hashing the raw IPs. This way you can handle duplicate entries efficiently. If you maintain an additional – separate - container just for the payload (item selection), things become inefficient. You could add a std::function to the template parameters, which gives you a reference to the payload. That is, you have a public struct with your selection, checkbox or whatever status information and the std::function will return this struct to you if you give it the index or iterator. If the std::function is null (if constexpr or inline, so it will be removed appropriately) you could keep your separate container. You might even consider giving the programmer the possibility to implement this functionality. In this case, you would not have a std::function which returns the struct, but instead functions for each status item - "bool isSelected( std::const_iterator ) const" etc. This way, one could choose to implement the selection more efficiently (less memory usage) in a custom list: **Selected: 5-12 15-15 19-233** In short, if you give the programmer the possibility to overwrite/disable the default behavior by passing custom functions (which receive the "raw" information = index/iterator), things can be handled A LOT more efficient (and easier, as you need less code when not treating your own containers like abstract objects).
edited May 8 at 1:10 pm
35
views
2
replies
2
followers
live preview
enter atleast 10 characters
WARNING: You mentioned %MENTIONS%, but they cannot see this message and will not be notified
Saving...
Saved
All posts under this topic will be deleted ?
Pending draft ... Click to resume editing
Discard draft