我正在尝试使用函数指针在C中实现类似Python的函数装饰器,但出现奇怪的分段错误。
想法是我们有一个decorator函数,该函数具有内部函数wrapper。然后,装饰器将some_function作为参数,将其与其他代码一起放入wrapper中,并返回wrapper函数。
Python中非常简单的函数装饰器:
def decorator(f):
def wrapper():
#do something before
f()
#do something after
return wrapper
def some_func():
print('Hello')
some_func = decorator(some_func)
[我知道Python与C不同,它把函数当作一类对象,但是我想知道是否可以通过使用函数指针在C中模拟相同类型的功能。
我尝试过此
void* do_twice(void (*func)())
{
auto void wrapper()
{
func();
func();
}
return &wrapper;
}
void some_func()
{ printf("Hello\n"); }
int main()
{
void (*fun_ptr)() = &some_func;
fun_ptr = decorator(fun_ptr);
fun_ptr();
return 0;
}
输出
Hello
Segmentation fault
现在是有趣的地方。如果我在wrapper中这样声明一个变量:
auto void wrapper()
{
int blah=5;
func();
func();
}
然后细分错误已修复。有人可以解释为什么会这样吗?显然我的指针做错了,代码也非常不稳定-似乎对main函数的不相关添加会导致分段错误再次弹出。
您允许我游览C ++吗?在这种语言中,有函子对象,它们是可以像函数一样调用的结构:
struct Wrapper
{
/* this is what allows the struct to be called like a function: */
void operator()(/* can define arbitrary parameters here */)
{ /* some implementation */ }
};
换句话说,这将允许您编写如下代码:
Wrapper w; // actually, C++ does more here than C, but that's out of
// scope of this question...
w(); // possible due to the operator() defined above
好,现在让我们扩展一下:
struct Wrapper
{
void(*m_f)(void); /* !!! */
void operator()(void) /* don't want any parameters... */
{
printf("before\n");
m_f();
printf("after\n");
}
};
[不想进一步深入,现在C ++开发人员将处理可访问性(这样,成员m_f
仅可在类内部使用),并提供所谓的构造函数(用于初始化m_f
成员适当地)。有了所有这些,使用包装器类的最终C ++代码可能看起来像:
Wrapper decorate(void(*f)(void))
{
return Wrapper(f); // the fore-mentioned constructor gets called
}
void test(void) { /* do something */ }
void demo(void)
{
Wrapper w = decorate(&test);
w();
}
为什么要游览?好吧,仔细看一下特定的一行:
void(*m_f)(void);
是的,有一个成员变量! Python实际上在内部做了类似的事情。
C的“问题”是您无法定义自己的函数调用运算符。只有本地人。因此,最大的问题是:函数指针存放在哪里?
您可以有一个结构,存储函数指针(类似于C ++解决方案),并将该指针传递给您自己的函数,以模拟函数调用运算符。实际上,C ++解决方案也没有做任何其他事情,仅是所有这些都隐藏在语法糖之后!
等效的C解决方案可能看起来像这样:
struct Wrapper
{
void(*m_f)(void);
};
void executeDecorated(Wrapper w)
{
printf("before\n");
w.m_f();
printf("after\n");
}
void demo(void)
{
Wrapper w = { &someFunction };
executeDecorated(w);
/* do whatever else ... */
executeDecorated(w);
}
这可能是您可以在C语言中获得的最接近的字母(除此之外,您可能会找到更好的名称)。
[如果具有单独的结构来存储函数指针会为您带来足够的好处,或者如果您只想直接传递函数指针(executeDecorated(&someFunction)
,假设函数已适当调整,则由您决定...