C 中受保护的函数调用
Guarded function calls in C
我想实现以下目标:
假设我们有一些函数foo
。我们还有两个函数 pre
和 post
分别检查它的前置条件和后置条件。
我们有一些 foo
函数调用。现在我想用检查条件是否满足的 "guarded" 调用替换每个 foo
调用。这必须使用编译器标志进行切换。递归 foo
调用也必须替换。
我能想到的最好的事情如下:
#ifdef NDEBUG
int foo_orig(int n);
int foo(int n)
{
int r;
assert(pre(n));
r = foo_orig(n);
assert(post(n, r));
return r;
}
int foo_orig(int n)
#else
int foo(int n)
#endif
{
/* The body of the function foo... */
foo(n); /* recursive call */
}
我知道这很笨拙和丑陋。有没有更好的方法呢?最好我只想要一个自动执行所有操作的宏,即
GUARD(foo, pre, post, arguments, return_type)
这可能吗?
编辑
建议将 pre
和 post
调用嵌入到 foo
正文中。这可能适用于 pre
,但对于 post
,我们需要用相当复杂的代码替换每个 return 语句。如果在 foo
body.
中更改了参数值,这也将不起作用
所以这更像是打字。我想要简单易用的东西。
你的意思是这样的?
#include <stdio.h>
#if 1
#define foo(...) GUARD(foo, pre, post, (__VA_ARGS__))
#define GUARD(F, PRE, POST, ARGS) (POST(F(PRE ARGS)))
//exactly equivalent to #define foo(...) post(foo(pre(__VA_ARGS__)))
#endif
int pre(int n) { printf("pre %d\n", n); return n; }
int post(int n) { printf("post %d\n", n); return n; }
int (foo)(int n) {
printf("%d\n", n);
return n == 0 ? 0 : foo(n - 1);
}
int main(void) {
foo(6);
}
#define
d 宏的一个特点是它们不允许递归扩展,所以你总是可以定义一个像 #define foo(X) foo(Y(X))
这样的宏——扩展中的 foo
是单独留下,并标记为不再更换。它专门用于此类用途,以便可以将函数调用无缝替换为同一调用的包装版本,而无需重命名,用于调试或其他此类目的。
(请注意,宏,不知道 C 的语法,如果有机会,也会替换 foo
的 声明 ,这是不好的;通常你' d 将宏定义放在主体之后或不同的文件中,但如果您需要递归包装器,您也可以使用鲜为人知的带括号的函数名语法来阻止它)
嗯。 NDEBUG
宏通常用于 抑制 调试代码(特别是断言),而不是导致包含调试代码。无论如何,是的,您可以创建一个宏,以可重用的方式为您执行此操作。但是,需要将 return 值插入到后置条件函数的参数中确实意味着您需要一个可变参数宏。
例如:
#ifdef NDEBUG
#define GUARD(ret, f, pre, post, ...) \
ret f ( __VA_ARGS__ )
#else
#define GUARD(ret, f, pre, post, ...) \
ret f##_orig ( __VA_ARGS__ ); \
ret f ( __VA_ARGS__ ) { \
ret r; \
assert(pre ( __VA_ARGS__ )); \
r = f##_orig ( __VA_ARGS__ ); \
assert(post(r, __VA_ARGS__ )); \
return r; \
} \
ret f##_orig ( __VA_ARGS__ )
#endif
您可以这样使用它:
GUARD(int, foo, pre, post, int n)
{
/* body of the real foo() */
}
如果您愿意为 pre
和 post
函数使用标准模式(例如 foo_pre()
和 foo_post()
),那么您可以从宏中省略那些参数并通过标记粘贴构造它们,因为 foo_orig
名称也被构造。然后使用看起来像这样:
GUARD(int, foo, int n)
{
/* body of the real foo() */
}
我想实现以下目标:
假设我们有一些函数foo
。我们还有两个函数 pre
和 post
分别检查它的前置条件和后置条件。
我们有一些 foo
函数调用。现在我想用检查条件是否满足的 "guarded" 调用替换每个 foo
调用。这必须使用编译器标志进行切换。递归 foo
调用也必须替换。
我能想到的最好的事情如下:
#ifdef NDEBUG
int foo_orig(int n);
int foo(int n)
{
int r;
assert(pre(n));
r = foo_orig(n);
assert(post(n, r));
return r;
}
int foo_orig(int n)
#else
int foo(int n)
#endif
{
/* The body of the function foo... */
foo(n); /* recursive call */
}
我知道这很笨拙和丑陋。有没有更好的方法呢?最好我只想要一个自动执行所有操作的宏,即
GUARD(foo, pre, post, arguments, return_type)
这可能吗?
编辑
建议将 pre
和 post
调用嵌入到 foo
正文中。这可能适用于 pre
,但对于 post
,我们需要用相当复杂的代码替换每个 return 语句。如果在 foo
body.
所以这更像是打字。我想要简单易用的东西。
你的意思是这样的?
#include <stdio.h>
#if 1
#define foo(...) GUARD(foo, pre, post, (__VA_ARGS__))
#define GUARD(F, PRE, POST, ARGS) (POST(F(PRE ARGS)))
//exactly equivalent to #define foo(...) post(foo(pre(__VA_ARGS__)))
#endif
int pre(int n) { printf("pre %d\n", n); return n; }
int post(int n) { printf("post %d\n", n); return n; }
int (foo)(int n) {
printf("%d\n", n);
return n == 0 ? 0 : foo(n - 1);
}
int main(void) {
foo(6);
}
#define
d 宏的一个特点是它们不允许递归扩展,所以你总是可以定义一个像 #define foo(X) foo(Y(X))
这样的宏——扩展中的 foo
是单独留下,并标记为不再更换。它专门用于此类用途,以便可以将函数调用无缝替换为同一调用的包装版本,而无需重命名,用于调试或其他此类目的。
(请注意,宏,不知道 C 的语法,如果有机会,也会替换 foo
的 声明 ,这是不好的;通常你' d 将宏定义放在主体之后或不同的文件中,但如果您需要递归包装器,您也可以使用鲜为人知的带括号的函数名语法来阻止它)
嗯。 NDEBUG
宏通常用于 抑制 调试代码(特别是断言),而不是导致包含调试代码。无论如何,是的,您可以创建一个宏,以可重用的方式为您执行此操作。但是,需要将 return 值插入到后置条件函数的参数中确实意味着您需要一个可变参数宏。
例如:
#ifdef NDEBUG
#define GUARD(ret, f, pre, post, ...) \
ret f ( __VA_ARGS__ )
#else
#define GUARD(ret, f, pre, post, ...) \
ret f##_orig ( __VA_ARGS__ ); \
ret f ( __VA_ARGS__ ) { \
ret r; \
assert(pre ( __VA_ARGS__ )); \
r = f##_orig ( __VA_ARGS__ ); \
assert(post(r, __VA_ARGS__ )); \
return r; \
} \
ret f##_orig ( __VA_ARGS__ )
#endif
您可以这样使用它:
GUARD(int, foo, pre, post, int n)
{
/* body of the real foo() */
}
如果您愿意为 pre
和 post
函数使用标准模式(例如 foo_pre()
和 foo_post()
),那么您可以从宏中省略那些参数并通过标记粘贴构造它们,因为 foo_orig
名称也被构造。然后使用看起来像这样:
GUARD(int, foo, int n)
{
/* body of the real foo() */
}