On Thu, Mar 11, 2021 at 02:22:45PM +0100, Jaroslav Kysela wrote:
Hm. I believe that you agree with the fact that we can make various algorithms to compare a pair of IDs for control elements. When focusing on fields except for numid, we can make the other algorithms against your implementation, since the ID structure is compound one. Each of the algorithms can return different result.
Here, I'd like to shift the discussion to the name of new API. Although it has the most common name, 'snd_ctl_id_compare', it just has one of comparison algorithms. I have a concern that the name can gives wrong idea to users that the ID structure for control element had design to be able to be compared by itself and it would just be a single comparison algorithm.
I suggest to rename the new API to express that it implements one of comparison algorithm. In a case of numid comparison, it would be 'snd_ctl_id_compare_by_numid()'. For your case, 'snd_ctl_id_compare_by_name_arithmetic' or something suitable.
Perhaps, we can add a third argument defining the sorting algorithm, so we don't bloat the symbol tables so much when we add a new sorting type (enum). It would mean that the function cannot be used as a direct argument to qsort(), but I think that the apps add usually an extra code to own callback depending on containers, anyway. Is it more appropriate for you?
I've already investigated the idea you describe, however I concluded that it has more complexity than convenience.
For example, the prototype would be:
``` int new_api(const snd_ctl_elem_id_t *l, const snd_ctl_elem_id_t *r, int (*algorithm)(const snd_ctl_elem_id_t *, const snd_ctl_elem_id_t *)); ```
For usage with qsort_r(3), programmer should do:
``` int my_algo(const snd_ctl_elem_id_t *l, snd_ctl_elem_id_t *r) { ... }
qsort_r(base, nmemb, size, new_api, my_algo); ```
On the other hand, the API has name to express itself appropriately and we have some of such APIs:
``` int the_api_by_algo_a(const snd_ctl_elem_id_t *l, const snd_ctl_elem_id_t *r); int the_api_by_algo_b(const snd_ctl_elem_id_t *l, const snd_ctl_elem_id_t *r); int the_api_by_algo_c(const snd_ctl_elem_id_t *l, const snd_ctl_elem_id_t *r); ... ```
The programmer selects one of them, then:
``` qsort(base, nmemb, size, the_api_by_algo_a); ```
Or select one of them dynamically if need:
``` int (*algo)(const snd_ctl_elem_id_t *, const snd_ctl_elem_id_t *);
switch (cond) { case A: algo = the_api_by_algo_a; break; case B: algo = the_api_by_algo_b; break; case C: algo = the_api_by_algo_c; break; default: return -EINVAL; }
qsort(base, nmemb, size, algo); ```
For the case of hctl/mixer container about which you mentioned, qsort_r(3) style is convenient for the case that programmer need to re-implement own comparison algorithm. However the decision is still up to the programmer, in short:
``` int my_algo(const snd_ctl_elem_id_t *l, const snd_ctl_elem_id_t *r, void *arg); qsort_r(base, nmemb, size, my_algo, my_arg); ```
Here, I think it more worth to share algorithms than keeping less entries in symbol table in shared library. Just the thought of it, I can devise some algorithms below:
* by numid * by name arithmetic (=your implementation) * by the words 'playback' and 'capture', case-insensitive or sensitive * by device and subdevice
Regards
Takashi Sakamoto