使用功能指针作为方法参数

问题描述 投票:2回答:3

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

这会产生错误“从不兼容的指针类型初始化”。有人可以告诉我我做错了什么,我应该怎么做吗?

c function-pointers
3个回答
4
投票

执行此操作时:

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

您实际上是在尝试进行call打印,而不是将指针传递给它。因此,您传递给traverse的实际上是print的返回值,而不是指向它的指针。

正确的调用将如下所示:

traverse(node, h, print);

然后在traverse中调用回调:

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

但是仍然有问题。函数print的类型与函数指针func_t不兼容。前者将FILE *作为其第一参数,而后者将void *作为其第二参数。与void *之间的隐式转换仅在它是转换的源或目标时才起作用。它不适用于功能参数。

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

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

1
投票

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

因此,要么必须更改功能指针类型才能与FILE*一起使用,要么必须更改打印功能才能与void*一起使用。

然而,编译器错误的原因是您在此处print调用了实际函数traverse(node, h, print(node, h));,而不是传递指向该函数的函数指针。由于此函数返回void,因此编译器会说“嘿,我不能将void参数传递给该函数,而需要一个函数指针”。只需将其更改为:

traverse(node, h, print);

然后traverse将通过传递的函数指针来调用函数。


0
投票

关于它的价值,也许您使用的是错误的编程语言:)您打算编写的几乎是C ++,应该是:

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

然后,遍历将是模板函数,因此您不必只传递一种函数,而可以传递任何函子(可调用对象,例如,实现调用运算符的类:]]]

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';
  });
}
© www.soinside.com 2019 - 2024. All rights reserved.