我正在尝试进行内存管理,并尝试创建可以以任何方式对其有所帮助的内容。现在,我试图考虑是否有任何方法可以重复使用Go in C中的“延迟”功能。
对于那些不知道延迟时间的人的快速示例:
package main
import "fmt"
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
return
}
将打印
3
2
1
因此,我正在考虑一些宏,这些宏会将带有参数的函数推入某些堆栈,并在调用函数exit时调用它们。像这样的东西:
int func(void)
{
MEMSTACK_INIT;
char * string = NULL;
node_t * node = NULL;
MEMSTACK_PUSH(free(string));
MEMSTACK_PUSH(NodeFree(&node));
<..>
switch (something)
{
case ONE : RETURN ERROR_ONE;
case TWO : RETURN ERROR_TWO;
case THR :
switch (something else)
{
<.. Many more code ..>
}
}
RETURN ERROR_GOOD;
}
有没有一种方法(当然,除了制作自己的预处理器外),可以将带有params的函数调用存储在某个地方?换句话说,我希望对之前的代码进行如下预处理:
int func(void)
{
<.. Some MEMSTACK initialisation stuff (if needed) ..>
char * string = NULL;
node_t * node = NULL;
<..>
switch (something)
{
case ONE :
free(string);
NodeFree(&node);
return ERROR_ONE;
case TWO :
free(string);
NodeFree(&node);
return ERROR_TWO;
case THR :
switch (something else)
{
<.. Many more code ..>
}
}
free(string);
NodeFree(&node);
return ERROR_GOOD;
}
对于需要在退出之前进行大量清理的函数,这将是一件好事。是的,是的,我知道goto cleanup
技巧。
我正在尝试进行内存管理,并尝试创建可以以任何方式对其有所帮助的内容。
一个好的方法是return
任何功能都只有一个。可能用标签标记(是的,也goto
可以,但是通常不鼓励这样做)。当然,请务必确保知道谁拥有分配的内存以及何时(以及在何处)转移所有权!
现在,让我们...
[..]在C中重复执行Go中的“延迟”功能。
首先,为了推迟调用,我们需要存储函数(指向它的指针)以及评估后的参数。由于C是静态类型的,因此我们需要将其统一为一个类型:
struct Fn {
void * parameters; // pointer to memory where the parameters are stored
void (*function)(void *); // pointer to function able to unpack parameters from above
struct Fn * next; // we want a stack, so ...
};
对于最终要推迟的每个函数,我们需要一种存储其参数的方法。因此,我们定义了一个struct
能够保存参数的功能和一个能够从中解压参数的功能struct
:
#define MAKE_DEFERRABLE(name, N, ...) \
struct deferred_ ## name ## _parameters { PARAMS(N, __VA_ARGS__) }; \
void deferred_ ## name (void * p) { \
struct deferred_ ## name ## _parameters * parameters = p; \
printf(" -- Calling deferred " #name "\n"); \
(void)name(CPARAMS(N)); \
}
该N
是参数的个数。有一些技巧可以从中推断出这一点__VA_ARGS__
,但是我将其留给读者作为练习。该宏包含另外两个宏扩展,PARAMS
和CPARAMS
。前者扩展为适合定义struct
内容的列表。后者扩展为代码以提取struct
成员作为参数:
#define PARAM_0(...)
#define PARAM_1(type, ...) type p1; PARAM_0(__VA_ARGS__)
#define PARAM_2(type, ...) type p2; PARAM_1(__VA_ARGS__)
#define PARAM_3(type, ...) type p3; PARAM_2(__VA_ARGS__)
#define PARAM_4(type, ...) type p4; PARAM_3(__VA_ARGS__)
#define PARAMS(N, ...) SPLICE(PARAM_, N)(__VA_ARGS__)
#define CPARAM_0
#define CPARAM_1 parameters->p1
#define CPARAM_2 parameters->p2, CPARAM_1
#define CPARAM_3 parameters->p3, CPARAM_2
#define CPARAM_4 parameters->p4, CPARAM_3
#define CPARAMS(N) SPLICE(CPARAM_, N)
如果我们要延迟具有4个以上参数的函数,则需要对此进行调整。这SPLICE
是一个很好的小帮手:
#define SPLICE_2(l,r) l##r
#define SPLICE_1(l,r) SPLICE_2(l,r)
#define SPLICE(l,r) SPLICE_1(l,r)
接下来,我们需要以某种方式存储延迟的函数。为简单起见,我选择动态分配它们,并保留指向最新的全局指针:
struct Fn * deferred_fns = NULL;
显然,您可以将其扩展到多个方向:使用(有限)静态存储,使其成为线程本地,使用per function deferred_fns
,使用alloca
,...
...但是这是简单的,不是生产准备就绪的(MISSING ERROR CHECKS)变体:
#define DEFER(name, N, ...) \
do { \
printf(" -- Deferring a call to " #name "\n"); \
if (deferred_fns == NULL) { \
deferred_fns = malloc(sizeof(*deferred_fns)); \
deferred_fns->next = NULL; \
} else { \
struct Fn * f = malloc(sizeof(*f)); \
f->next = deferred_fns; \
deferred_fns = f; \
} \
deferred_fns->function = &(deferred_ ## name); \
struct deferred_ ## name ##_parameters * parameters = malloc(sizeof(*parameters)); \
SPARAMS(N,__VA_ARGS__); \
deferred_fns->parameters = parameters; \
} while(0)
这只是分配一个new struct Fn
,使其成为堆栈的顶部(读取单链接列表deferred_fns
),并相应地设置其成员。重要信息SPARAMS
将参数保存到相应的内容中struct
:
#define SPARAM_0(...)
#define SPARAM_1(value, ...) parameters->p1 = (value); SPARAM_0(__VA_ARGS__)
#define SPARAM_2(value, ...) parameters->p2 = (value); SPARAM_1(__VA_ARGS__)
#define SPARAM_3(value, ...) parameters->p3 = (value); SPARAM_2(__VA_ARGS__)
#define SPARAM_4(value, ...) parameters->p4 = (value); SPARAM_3(__VA_ARGS__)
#define SPARAMS(N, ...) SPLICE(SPARAM_, N)(__VA_ARGS__)
注意:这可以通过使参数从最后到第一个进行评估来固定参数评估的顺序。C不强制执行评估命令。
最后,剩下的就是运行这些延迟函数的便捷方法:
void run_deferred_fns(void) {
while (deferred_fns != NULL) {
deferred_fns->function(deferred_fns->parameters);
free(deferred_fns->parameters);
struct Fn * bye = deferred_fns;
deferred_fns = deferred_fns->next;
free(bye);
}
}
void foo(int x) {
printf("foo: %d\n", x);
}
void bar(void) {
puts("bar");
}
void baz(int x, double y) {
printf("baz: %d %f\n", x, y);
}
MAKE_DEFERRABLE(foo, 1, int);
MAKE_DEFERRABLE(bar, 0);
MAKE_DEFERRABLE(baz, 2, int, double);
int main(void) {
DEFER(foo, 1, 42);
DEFER(bar, 0);
DEFER(foo, 1, 21);
DEFER(baz, 2, 42, 3.14);
run_deferred_fns();
return 0;
}
为了实现与示例相同的行为,请创建deferred_fns
一个局部变量,并将其作为参数传递给run_deferred_fns
。包装在简单的宏中,完成:
#define PREPARE_DEFERRED_FNS struct Fn * deferred_fns = NULL;
#define RETURN(x) do { run_deferred_fns(deferred_fns); return (x); } while (0)
欢迎疯狂。
注意:我的解决方案在“源级别”上工作。我的意思是,您需要在源代码中指定可延迟函数。例如,这意味着您不能推迟通过加载的函数dlopen
。如果您愿意,也可以使用另一种方法在ABI级别上进行操作:avcall,libffcall的一部分。
现在,我真的需要我的括号...很多(())))(()((((
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句