在下面的模拟示例中,我尝试构造一个
generic_processor
接口,其中调用者将对象指针传递到处理器中的 void*
参数中,并且调用者显式地将“动作”转换为 void(*gfp)(void)
(通用函数指针),这正是 generic_processor
所期望的。
“具体操作”对于跨类型的每个操作具有几乎相同的签名,因为它们采用对象指针,可能还有一些其他参数并返回
int
。请注意,具体操作采用具体类型的对象指针。
这是关键步骤,我对此表示怀疑:generic_processor
将
gfp
参数转换为函数指针
typedef
,其签名与具体操作函数相匹配,除了对象指针现在是一个
void*
。
正如这个答案中引用的:
https://stackoverflow.com/a/189126/1087626
所以,问题的核心是:在这种情况下“不兼容”是什么意思?766 指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再转换回来;
- 767 结果应等于原始指针。
- 768 如果使用转换后的指针来调用其类型与所指向类型不兼容的函数,则行为未定义。
在
generic_processor
中,我正在转换为函数指针签名,该签名与原始具体签名相同,只是它具有
void*
作为第一个参数,而不是
circle*
或
square*
。这个定义清楚吗?
模拟示例:
#include <stdio.h>
// types
typedef struct {
int a;
double d;
} circle;
typedef struct {
int b;
float f;
} square;
// ... 10 types,.. different sizeof()
// concrete API
int open_circle(circle* c) {
printf("opening circle: %d: %f\n", c->a, c->d);
return c->a;
}
int open_square(square* s) {
printf("opening square: %d: %f\n", s->b, s->f);
return s->b;
}
int send_circle(circle* c, const char* msg) {
printf("sending circle: %d: %f: %s\n", c->a, c->d, msg);
return -c->a;
}
int send_square(square* s, const char* msg) {
printf("sending square: %d: %f: %s\n", s->b, s->f, msg);
return -s->b;
}
// ten more operations for each type
// "genericised" function pointer types (note the void* params!!)
typedef int (*open_fpt)(void* o);
typedef int (*send_fpt)(void* o, const char*);
typedef void (*gfp)(void); // generic function pointer
int generic_processor(void* obj, gfp open, gfp send) {
int sum = 0;
sum += ((open_fpt)open)(obj);
sum += ((send_fpt)send)(obj, "generically sent");
return sum;
}
int main() {
circle c = {2, 22.2};
square s = {3, 33.3F};
int net = 0;
net += generic_processor(&c, (gfp)open_circle, (gfp)send_circle);
net += generic_processor(&s, (gfp)open_square, (gfp)send_square);
printf("net %d\n", net);
return 0;
}
用 gcc 13.2 编译:工作正常,没有警告:
gcc -std=c99 -g -o gfp tests/gfp.c -Wall -Wextra -pedantic -fsanitize=address,leak,undefined && ./gfp
opening circle: 2: 22.200000
sending circle: 2: 22.200000
opening square: 3: 33.299999
sending square: 3: 33.299999
net 0
open_fpt
或
send_fpt
兼容,因为它们采用单个
void *
参数,并且相关函数采用指向不同结构类型的单个指针。 所以这个转换是无效的并导致未定义的行为。对象指针和函数指针之间的转换也是未定义的。 特别是,当需要
generic_processor
参数时,您不能将函数指针传递给
void *
,也不能在函数中进行另一个方向的转换。您需要做的是更改函数以采用
void *
参数,然后将参数转换为正确的类型。 例如:
int open_circle(void *p) {
circle* c = p;
printf("opening circle: %d: %f\n", c->a, c->d);
return c->a;
并且您需要更改 generic_processor
以采用函数指针类型:
int generic_processor(void* obj, open_fpt open, send_fpt send) {
int sum = 0;
sum += open(obj);
sum += send(obj);
return sum;
}