该解决方案由两部分组成,一部分是静态库,它接收来自库用户的struct实例。库不知道结构类型是什么,它只知道将有两个具有特定名称的函数指针。
图书馆代码
预编译库无法知道用户结构的类型,因此通过void*
接收
void save(void *data) {
// library will save/cache user's object
data->registered(); // if register successful
}
void remove(void *data) {
// library will remove the object from memory
data->remove(); // if removed successful
}
图书馆代码的用户
struct Temp { // random order of fields
void (*custom1)();
void (*registered)();
void (*custom2)();
void (*remove)();
void (*custom3)();
}
void reg() {
printf("registered");
}
void rem() {
printf("removed");
}
void custom1() {}
void custom2() {}
void custom3() {}
var temp = malloc(struct Temp, sizeof(struct Temp));
temp->registered = reg;
temp->remove = rem;
temp->custom1 = custom1; // some custom functions
temp->custom2 = custom2;
temp->custom3 = custom3;
// calling library code
save(temp);
remove(temp);
Q值。有没有办法让图书馆知道如何迭代和浏览成员字段,看看是否有指向这种功能的指针并将其称为可用。
答案 0 :(得分:2)
库是否有办法知道如何迭代并浏览成员字段,看看是否有指向此类函数的指针并将其称为可用。
没有。
最好的办法是在库中创建一个包含这些成员的结构,并传递该结构而不是void*
。
答案 1 :(得分:1)
正如@immibis所说,如果编译器不知道传递给函数的数据类型是什么,那么就没有办法(即编译器没有办法证明编译这样的代码)。
由于您希望将对象传递到库而不存储有关库中每个对象类型的信息,因此您可以fake polymorphism in C执行以下操作:
#ifndef _CALLBACK_H_
#define _CALLBACK_H_
typedef struct {
void (*registered)();
void (*removed)();
} ICallback;
#endif _CALLBACK_H_
#ifndef _PRE_COMP_H_
#define _PRE_COMP_H_
#include "callback.h"
void save(ICallback* data);
void remove(ICallback* data);
#endif /* _PRE_COMP_H_ */
#include <stdlib.h> /* NULL */
#include "callback.h"
#include "pre_comp.h"
void save(ICallback *data) {
if (NULL != data && NULL != data->registered) {
data->registered(); // if register successful
}
}
void remove(ICallback *data) {
if (NULL != data && NULL != data->removed) {
data->removed(); // if removed successful
}
}
#include <stdio.h>
#include "pre_comp.h"
#include "callback.h"
struct Temp {
ICallback base; // has to be defined first for this to work
void (*custom1)();
void (*custom2)();
void (*custom3)();
};
// calling library code
void reg() {
puts("registered");
}
void rem() {
puts("removed");
}
int main() {
struct Temp data = {{reg, rem}};
save((ICallback*)&data);
remove((ICallback*)&data);
}
gcc pre_comp.c main.c
registered removed
答案 2 :(得分:1)
如果库有关于可能的结构类型的0信息,那么你 做不到。图书馆必须以某种方式得到信息或抵消。
我能想到的唯一方法是:
register
成员都拥有相同的原型我创建了一个这个
的例子#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
// function that does not know anything about any struct
void reg(void *data, size_t offset)
{
uintptr_t *p = (uintptr_t*) (((char*) data) + offset);
void (*reg)() = (void(*)()) *p;
reg();
}
struct A {
int c;
void (*reg)();
};
struct B {
int b;
int c;
void (*reg)();
};
void reg_a()
{
printf("reg of A\n");
}
void reg_b()
{
printf("reg of B\n");
}
int main(void)
{
struct A a;
struct B b;
a.reg = reg_a;
b.reg = reg_b;
reg(&a, offsetof(struct A, reg));
reg(&b, offsetof(struct B, reg));
return 0;
}
打印:
$ ./c
reg of A
reg of B
我用valgrind运行它,我没有得到任何错误或警告。我不确定是否
这违反了某种严格的别名规则或产生未定义的行为
因为uintptr_t*
次转化,但至少它似乎有效。
然而,我认为,更清洁的解决方案是重写register
(顺便说一句。register
是C中的关键字,您不能将其用于函数名称)函数
接受函数指针和可能的参数,如下所示:
#include <stdio.h>
#include <stdarg.h>
void reg(void (*func)(va_list), int dummy, ...)
{
if(func == NULL)
return;
va_list ap;
va_start(ap, dummy);
func(ap);
va_end(ap);
}
void reg1(int a, int b)
{
printf("reg1, a=%d, b=%d\n", a, b);
}
void vreg1(va_list ap)
{
int a = va_arg(ap, int);
int b = va_arg(ap, int);
reg1(a, b);
}
void reg2(const char *text)
{
printf("reg2, %s\n", text);
}
void vreg2(va_list ap)
{
const char *text = va_arg(ap, const char*);
reg2(text);
}
int main(void)
{
reg(vreg1, 0, 3, 4);
reg(vreg2, 0, "Hello world");
return 0;
}
这有输出:
reg1, a=3, b=4
reg2, Hello world
请注意,reg
有一个dummy
参数。我之所以这样做是因为
stdarg
说:
va_start()
:[...]
因为可以在
va_start()
宏中使用此参数的地址, 不应将其声明为寄存器变量,也不应声明为 函数或数组类型。
答案 3 :(得分:0)
除了指向结构的void指针之外,您还可以采用类似于qsort的方法并传递函数指针。
这是qsort的函数原型,它是一个可用于对任何类型的数组进行排序的函数:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
它需要一个执行比较的函数指针,因为没有它,qsort就不会知道如何比较两个对象。
这可以通过这样的函数原型应用于您的任务:
int DoFoo(void *thing, void (*register)(void *), void (*remove)(void *))
此函数获取一个指向结构的void指针,然后在需要注册或删除该结构时调用它们可以调用的两个函数。将函数作为结构的成员不是必需的,我通常不推荐它。我建议阅读qsort,因为它的功能类似于你想要做的事情。