On Monday, August 27, 2018 at 5:56:15 AM UTC-4, Anton Shepelev wrote:
> Menachem:
BTW, I see the same issue that Google Groups thinks it's in Hebrew. Sometimes AI's can be unbelievably stupid. Lo chashuv (Hebrew for "not important").
>
> >
> >What I have so far:
> >
> > // Iterate through the Map's entries; flag is THRU, WHILE, or UNTIL
> > int each_entry(struct Map *map,
> > int(*fn)(const char *key, void *data), int flag)
> > {
> > int count = 0;
> > while (/* There are more entries */) {
> > // TODO: Find next entry - implementation specific
> > int retval = fn(entry->key, entry->data);
> > if (flag == WHILE && !retval || flag == UNTIL && retval)
> > break;
> > else
> > ++count;
> > }
> > return count; // count < size, if broke out early.
> > }
> >
> >Now, how do I write the helper method each_value()?
>
> How about stopping at that and using each_entry for looping
> both over the values and over the keys? How about
> implementing the whole structure via physical subtyping and
> accessing it with normal C loops?
This function is part of the public API, too, though I think I need to add a third parameter to the callback function, supplying user data such as another object to compare this to.
I'm not sure what you mean by "implementing ... via physical subtyping". If you mean I should supply an Iterator type that knows about my implementation, and implement all foreach() functions via that Iterator, that is one option I hadn't considered. It still means duplicating some of the looping construct in each foreach(), but for my specific situation none of that will involve the Map implementation, only the Iterator interface.
> >The solution I came up with, that looks like it should work:
> >
> > static int (*_usr_data_fn)(void *data);
> > static int fn_entry_value(const char *key, void *data)
> > {
> > return _usr_data_fn(data);
> > }
> >
> > int each_value(struct Map *map, int(*fn)(void *data), int flag)
> > {
> > _usr_data_fn = fn;
> > int count = each_entry(map, fn_entry_value, flag);
> > _usr_data_fn = (int (*)(void *)) 0;
> > }
> >
> >In addition to being very inelegant, however, this stores
> >the user-supplied single-parameter function in a static
> >location, so it isn't threadsafe for use in a library.
>
> Pass that function wrapped in the data parameter, possibly
> together with some data pointer, i.e.:
>
> typedef int (*_usr_data_fn)(void *data);
> struct ev_data_t
> { _usr_data_fn fn;
> void* data;
> }
> /* ... */
> struct ev_data_t *evdata;
> /* ... */
> evdata->fn( evdata->data );
(As corrected elsethread.)
If I understand you correctly, using an extra data parameter - which I suppose I need in any case - can do what I need. The extra data parameter to the entry iterator will be both the user's function pointer and the user's extra data to that function.
struct fn_data_t {
int (*fn)(void *data, void *extra);
void *extra;
};
static int fn_entry_value(const char *key, void *data, void *extra)
{
struct fn_data_t *fn_data = extra;
return fn_data->fn(data, fn_data->extra);
}
int each_value(struct Map *map, int(*fn)(void *data, void *extra),
void *extra, int flag)
{
struct fn_data_t fn_data = { fn, extra };
return each_entry(map, fn_entry_value, &fn_data, flag);
}
And of course changing the signature of each_entry() to match.
That looks like it might work. A possible use case would be:
_Bool contains_value(struct Map *map, char *str)
{
int count = each_value(map, strcmp, str, WHILE);
return count < map->size; // We stopped when strcmp return 0
}
The idea was to have the same callbacks for several sorts of containers. Adding the extra data parameter is work, but obviously necessary anyhow.
Thanks a lot, Anton. In SO parlance, I "accept" this answer.
-- Menachem Salomon