使用函数指针作为方法参数
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';
});
}
我正在尝试使用函数指针来使我的代码更有效率。但是,我是这个概念的新手,无法将其用作另一个函数中的参数。
这给出了错误 "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';
});
}