«Перераспределение» аргументов функции - PullRequest
1 голос
/ 20 марта 2009

Я делаю это присваивание класса, используя классический C, и застрял с этой проблемой в отношении функций обратного вызова, которые принимают переменные аргументы count и type.

По сути, я работаю над хэшированным деревом (деревом, где каждый из узлов является хеш-деревом), и у меня есть определенная стратегия обхода, которая будет использоваться несколько раз для разных целей, поэтому я реализовал ее как ht_walk(HashTree tree, (*callback)(Element e)), так что функция, вызываемая как обратный вызов, будет обрабатывать Элемент любым необходимым способом.

Проблема в том, что в большинстве ситуаций в моей проблеме функция обратного вызова должна принимать разные аргументы. Я знаю, как спроектировать функцию со списком переменных аргументов, используя функции 'variadic' (используя stdarg, printf-way), но я не знаю, как «передать» эти аргументы в функцию обратного вызова.

Позвольте мне привести конкретный пример: предположим, у меня есть функция обратного вызова с именем addToList(Element e, List list), и теперь мое объявление ht_walk теперь ht_walk(HashTree tree, (*callback)(Element e), ...). Предположим, я хочу использовать ht_walk, как в следующем фрагменте:

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);

Есть ли способ сделать это? Заранее спасибо!

Ответы [ 5 ]

3 голосов
/ 20 марта 2009

Вы можете решить эту проблему одним из двух способов.

Самый распространенный, понятный и понятный метод - использовать структуру user:

void ht_walk(HashTree tree, void (*callback)(Element e, void *user), void *user);

void addToList(Element e, void *arg)
{
    STATIC_ASSERT(sizeof(void *) >= sizeof(List));

    List list = arg;

    /* ... */
}

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);

Другой метод - принять va_list:

#include <stdarg.h>

void ht_walk(HashTree tree, void (*callback)(Element e, va_list args), ...)
{
    for(..)
    {
        va_list args;
        va_start(args, callback);
        callback(element, args);
        va_end(args);
    }
}

void addToList(Element e, va_list args)
{
    List list = va_arg(args, List);

    /* ... */
}

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);
1 голос
/ 20 марта 2009

Я не думаю, что вы хотите передать произвольное количество аргументов. Я думаю, что у вас есть случай, когда у вас есть несколько типов обратного вызова. В вашем примере вы передаете список, но вы также упоминаете, что иногда передаете элемент. Таким образом, мне кажется, что вы действительно хотите сделать, это объявить свой обратный вызов, чтобы взять пустоту * и флаг, чтобы указать, что это такое. Что-то вроде:

void callback(void* arg, int type) {
    switch (type) {
    case ARG_TYPE_LIST:
        List* list = (List*)arg;
        ...
        break;
    case ARG_TYPE_ELEMENT:
        Element* ele = (Element*)arg;
        ...
        break;
    ...
}

Если вы передаете вещи в виде списка, список должен знать количество, поэтому вам не нужно беспокоиться о его передаче.

В качестве альтернативы, вы можете определить несколько типов обратного вызова и всегда вызывать правильный, основанный на том, какую обработку вы должны выполнить.

Если вы не ограничены прямой C (то есть вы можете использовать C ++), вы можете исследовать, используя boost :: bind , поскольку это очень полезно для такого рода вещей.

1 голос
/ 20 марта 2009

Существует ряд функций и макросов для работы с переменными аргументами (например, va_start). У GNU есть хорошее руководство здесь

При этом, похоже, вы описываете своего рода шаблон посетителя. Я не уверен, что мне нравится здесь использование списков аргументов с переменным числом аргументов. Если вы обычно не можете определить, какие параметры ваш обратный вызов будет получать последовательно для всех узлов, у вас начнутся проблемы.

0 голосов
/ 20 марта 2009

Я бы передал void *, так как я не думаю, что вы можете использовать переменное число аргументов в функции обратного вызова. Хотя я могу ошибаться.

typedef void (*tree_walk_callback)(Element e, void *data);

add_element(Element e, void *data)
{
    List *list = (List *)data;
    add_element_to_list(list, e);
}

...in your function...
List my_list;
ht_walk(my_tree, &add_element, (void *)&my_list);

Если пользователь хочет передать несколько аргументов, он передаст структуру, содержащую все, что ему нужно.

0 голосов
/ 20 марта 2009

Я полагаю, вы используете макрос va_list. См. справочную страницу по stdarg .

Прошло много времени с тех пор, как я попробовал это ...

...