“重传” 函数参数

发布于 2024-07-16 07:43:28 字数 717 浏览 5 评论 0原文

我正在使用经典 C 进行此类分配,并且遇到了有关采用可变参数计数和类型的回调函数的问题。

基本上,我正在研究哈希树(每个节点都是哈希树的树),并且我有一定的遍历策略,该策略将出于不同目的多次使用,因此我将其实现为 ht_walk (HashTree 树,(*callback)(Element e)),以便作为回调调用的函数将以任何必要的方式处理 Element。

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

让我提供一个具体的例子:假设我有一个名为 addToList(Element e, List list) 的回调函数,并且我的 ht_walk 声明现在是 ht_walk(HashTree tree, (*callback)(元素 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);

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

I'm doing this class assignment, using classic C, and am stuck with this problem about callback functions that take variable arguments count and type.

Basically, I'm working on a Hashed Tree (a tree where each of the nodes is a hash tree), and I have a certain traversal strategy that will be used multiple times for different purposes, so I implemented it as ht_walk(HashTree tree, (*callback)(Element e)), so that the function called as callback will process the Element whatever way necessary.

Problem being, in most situations in my problem the callback function will have to take different arguments. I know how to design a function with a variable argument list using 'variadic' functions (using stdarg, printf-way), but I don't know how to 'repass' these arguments to the callback function.

Let me provide a concrete example: suppose I have a callback function called addToList(Element e, List list), and that my ht_walk declaration is now ht_walk(HashTree tree, (*callback)(Element e), ...). Consider I want to use ht_walk like in the following snippet:

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);

Is there a way to do this? Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

青朷 2024-07-23 07:43:28

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

最常见、易于理解且干净的方法是使用“用户”结构:

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);

You can solve this using one of two methods.

The most common, understandable, and clean method is to use a 'user' structure:

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);

Another method is to accept 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);
遗失的美好 2024-07-23 07:43:28

有一系列函数和宏用于处理可变参数(例如,va_start)
GNU 有一个很好的指南

您似乎在描述某种访客模式。 我不确定我喜欢在这里使用可变参数列表。 如果您通常无法确定所有节点的回调将一致获得哪些参数,那么您一开始就会遇到麻烦。

There is a series of functions and macros for dealing with variadic arguments (e.g., va_start)
GNU has a good guide here

That being said, you seem to be describing sort of a visitor pattern. I am not sure I like the use of variadic argument lists here. If you can't typically establish what parameters your callback would be getting consistently for all nodes, you are in trouble to begin with.

妳是的陽光 2024-07-23 07:43:28

我不认为你想要做的是传递任意数量的参数。 我认为这里的情况是有多种类型的回调。 在您的示例中,您传递了一个列表,但您也提到有时您会传递一个元素。 所以在我看来,你真正想做的就是声明你的回调函数采用 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,因为它对于这类事情非常有用。

I don't think what you want to do is pass an arbitrary number of arguments. I think what you have here is a case where you have multiple types of callback. In your example, you're passing a List, but you also mention sometimes you'll pass an Element. So it seems to me what you really want to do is declare your callback to take a void* and a flag to indicate what it is. Something like:

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;
    ...
}

If you're passing things as a list, the list should know the count, so you don't have to worry about passing that.

Alternatively, you could define multiple types of callback and always callback the right one based on what sort of processing you need to do.

If you're not restrained to straight C (i.e. you can use C++) you may investigate using boost::bind, as it is very useful for this sort of thing.

杯别 2024-07-23 07:43:28

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

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

I would pass in a void *, as I don't think you can use variable number of arguments in a callback function. I could be wrong though.

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);

If the user wants to pass multiple arguments, then they would pass a struct through containing whatever they need.

夏尔 2024-07-23 07:43:28

我相信您使用了 va_list 宏。 请参阅stdarg 的手册页

自从我尝试这个以来已经有一段时间了......

I believe you use the va_list macro. See the man page for stdarg.

It has been a while since I tried this though...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文