使用函数指针作为方法参数

Using Function Pointer as method parameter

我正在尝试使用函数指针来使我的代码更有效率。但是,我是这个概念的新手,无法将其用作另一个函数中的参数。

这给出了错误 "initialization from incompatible pointer type"。 谁能告诉我我做错了什么以及我应该怎么做?

当你这样做时:

traverse(node, h, print(node, h));

您实际上是在尝试调用 打印而不是传递指向它的指针。因此,您传递给 traverse 的实际上是 print 的 return 值,而不是指向它的指针。

正确的调用应该是这样的:

traverse(node, h, print);

然后在 traverse 中调用回调:

void traverse(node_t *node, void *param, func_t func)
{
    ...
    func(node,param);
    ...
}

不过还是有问题。函数类型 print 与函数指针 func_t 不兼容。前者将 FILE * 作为其第一个参数,而后者将 void * 作为其第二个参数。隐式转换 to/from a void * 仅在它是转换的源或目标时才有效。它不适用于函数参数。

假设您需要回调来处理各种不同的类型,您可以将 print 函数更改为接受一个 void * 并在函数内部转换参数。

void print(node_t *node, void *param)
{
    FILE *f = param;  // no cast needed
    ...

首先:仅仅因为 void* 可以用来代替任何其他 对象指针 ,这并不意味着 函数指向接受 void* 的函数的指针 与接受 FILE*.

的另一个函数兼容

因此您要么必须更改函数指针类型以使用 FILE*,要么必须更改打印函数以使用 void*

然而,编译器错误的原因是您在此处 print 调用了实际函数 traverse(node, h, print(node, h));,而不是将函数指针传递给它。因为这个函数 returns void,编译器说 "hey I can't pass a void parameter to this function expecting a function pointer"。只需将其更改为:

traverse(node, h, print);

然后traverse会通过传入的函数指针调用函数

尽管如此,也许您使用了错误的编程语言 :) 您打算编写的几乎是 C++,它应该是:

traverse(node, [node,h]{ print(node, h); });

然后,traverse 将是一个模板函数,这样您就不会局限于只传递一种函数,而是可以传递任何仿函数(一个可调用对象,例如 class 实现呼叫运营商):

template <typename Fun> void traverse(node_t *, Fun &&f);

但是您使用了函数指针,因为它是 C 语言,并且函数指针与 C++ 闭包不同:它不能携带任何参数。因此,传递参数是你的责任。我想 FILE* 参数真的来自节点?这是您要表示的目录树吗?

typedef struct {
  node_t **children;  // null-terminated array of child node pointers
  const char *name;
} node_t;

typedef void (*cnode_observer_t)(const node_t *, int level, void *);

void ctraverse_level(const node_t *node, int level, void *extra, 
                     cnode_observer_t cobserver)
{
  cobserver(node, extra);
  node_t **child = node->children;
  ++level;
  while (*child) {
    ctraverse_level(*child++, level, extra, cobserver);
  }
}

void ctraverse(const node_t *node, void *extra, cnode_observer_t cobserver) {
  ctraverse_level(node, 0, extra, cobserver);
}

void node_printer(const node_t *node, int level, void *file) {
  assert(node && file);
  FILE *f = file;
  fprintf(f, "%*c%s\n", level, ' ', node->name);
}

void test(const node_t *node, FILE *file) {
  ctraverse(node, file, node_printer);
}

为了完整起见,下面是它在 C++ 中的样子:

struct Node {
  std::vector<Node> children;
  std::string name;
};

template <typename Obs>
void ctraverse(const Node &node, F fun, int level = 0) {
  fun(node, level);
  ++level;
  for (auto &n : node.children) {
    ctraverse(n, fun);
  }
}

void test(const Node &node, std::ostream &out) {
  traverse(node, [&](auto &node, int level) { 
    out << setw(level) << setfill(' ') << " " << node.name << '\n';
  });
}