“重绕”函数参数

时间:2009-03-20 01:04:03

标签: c variadic-functions tree-traversal

我正在使用经典的C进行这个类赋值,并且我遇到了关于带有变量参数count和type的回调函数的问题。

基本上,我正在处理一个哈希树(一棵树,其中每个节点都是一个哈希树),我有一个特定的遍历策略,它将被多次用于不同的目的,所以我把它实现为{ {1}},因此被称为回调的函数将以任何必要的方式处理Element。

问题是,在我的问题中的大多数情况下,回调函数必须采用不同的参数。我知道如何使用'variadic'函数(使用stdarg,printf-way)设计带有变量参数列表的函数,但我不知道如何将这些参数“重新”回到回调函数。

让我提供一个具体的例子:假设我有一个名为ht_walk(HashTree tree, (*callback)(Element e))的回调函数,而我的ht_walk声明现在是addToList(Element e, List list)。考虑我想在下面的代码片段中使用ht_walk:

ht_walk(HashTree tree, (*callback)(Element e), ...)

有办法做到这一点吗?提前谢谢!

5 个答案:

答案 0 :(得分:3)

您可以使用以下两种方法之一解决此问题。

最常见,最易理解,最简洁的方法是使用“用户”结构:

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 :(得分:1)

有一系列用于处理可变参数的函数和宏(例如,va_start) GNU有一个good guide here

话虽如此,你似乎在描述一种访客模式。我不确定我喜欢在这里使用可变参数列表。如果你通常不能确定你的回调将为所有节点一致地获得什么参数,那么你就很难开始。

答案 2 :(得分:1)

我不认为你想要做的是传递任意数量的参数。我认为你在这里有一种情况,你有多种类型的回调。在你的例子中,你传递了一个List,但是你有时也提到你会传递一个Element。所以在我看来你真正想做的是声明你的回调取一个void *和一个标志来表明它是什么。类似的东西:

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进行调查,因为它对此类事情非常有用。

答案 3 :(得分:0)

我相信您使用va_list宏。请参阅the man page for stdarg

我试过这个已经有一段时间了......

答案 4 :(得分:0)

我会传入一个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);

如果用户想要传递多个参数,那么他们会传递一个包含所需内容的结构。