让我们说我希望C macro适用于任何类型。 我正在使用GCC编译器(> = 4.6)并且可以使用GNU99宏。
//code...
any_type_t *retVal = function_that_runs_very_long_time(a, b, &&c, **d, &e, *f);
//other code...
用于TIMER的宏的用法可以像这样查看
//code...
any_type_t *retVal =
TIMER(
function_that_runs_very_long_time(a, b, &&c, **d, &e, *f),
"TIMING FOR VALUE <%d, %d>", a, b
);
//other code...
因此TIMER必须返回给定函数的值和其运行的打印持续时间。
具有void
返回类型的函数存在问题。
我显然可以有两个宏,如TIMER_TYPE和TIMER_VOID,但我想使用单一的时间函数和任何返回值。
感谢您的建议。
此TIMER宏的编辑示例
#define TIMER(expr, fmt_msg, ...) \
({ \
struct timeval before, after; \
uint64_t time_span; \
int time_span_sec, time_span_usec; \
gettimeofday(&before, NULL); \
typeof(expr) _timer_expr__ = (expr); \ // <- static if?
gettimeofday(&after, NULL); \
time_span = (after.tv_sec * 1000000 + after.tv_usec) \
- (before.tv_sec * 1000000 + before.tv_usec); \
time_span_sec = time_span / 1000000; \
time_span_usec = time_span % 1000000; \
TRACE(fmt_msg "\n%s : %d.%d seconds", \
#expr, time_span_sec, time_span_usec, ...); \
_timer_expr__; \
})
答案 0 :(得分:12)
多么有趣的问题,恭喜!
经过几次实验,我找到了一个使用GCC内含子__builtin_types_compatible_p
and __builtin_choose_expr
的解决方案。
__builtin_types_compatible_p
引用GCC手册:
内置函数:
int __builtin_types_compatible_p (type1, type2)
您可以使用内置函数
__builtin_types_compatible_p
来确定两种类型是否相同。如果类型
1
和type1
(类型,而不是表达式)的非限定版本兼容,则此内置函数将返回type2
,否则为0
。 此内置函数的结果可用于整数常量表达式。此内置函数会忽略顶级限定符(例如
const
,volatile
)。例如,int
相当于const int
。
以下是我们如何检查&#34; void
ness&#34;。
#define __type_is_void(expr) __builtin_types_compatible_p(typeof(expr), void)
__builtin_choose_expr
内置函数:
type __builtin_choose_expr (const_exp, exp1, exp2)
您可以使用内置函数
__builtin_choose_expr
根据常量表达式的值来计算代码。如果exp1
(整数常量表达式)非零,则此内置函数返回const_exp
。否则返回exp2
。此内置函数类似于C中的
? :
运算符,但返回的表达式的类型未按升级规则更改。此外,内置函数不会评估未选择的表达式。例如,如果const_exp
的计算结果为true,则即使exp2
有副作用,也不会对其进行评估。如果返回
exp1
,则返回类型与exp1
的类型相同。同样,如果返回exp2
,则其返回类型与exp2
相同。
所以__builtin_choose_expr
内在就像一个&#34;静态开关&#34;在编译时评估。
我不会在此粘贴您的TIMER
宏,但我认为它可以将其拆分为两个版本:一个用于void expr
,另一个用于其余版本。这里只是存根,它们评估表达式并产生相同类型的结果。
#define __DO(expr) \
({ typeof(expr) __ret; __ret = (expr); __ret; })
#define __DO_VOID(expr) \
(void) (expr)
现在我们可以在两个实现之间静态切换,具体取决于表达式的实际类型。但事实上,天真的解决方案并不起作用,见下文。
#define DO(expr) \
__builtin_choose_expr(__type_is_void(expr), \
__DO_VOID(expr), \
__DO(expr)) # won't work
尝试编译此代码传递void表达式会出现以下错误:
test.c:28:9: error: variable or field ‘__ret’ declared void
test.c:28:9: error: void value not ignored as it ought to be
虽然选择了__DO_VOID
,但__DO
会产生错误。手动中描述了此行为:
...未使用的表达式(
exp1
或exp2
取决于const_exp
的值)可能仍会生成语法错误。这可能会在未来的修订中发生变化。
诀窍是将原始的void expr
替换为一些非void值,以便能够编译__DO
的情况(当expr
无效时,无论如何都是死代码)
#define __expr_or_zero(expr) __builtin_choose_expr(__type_is_void(expr), 0, (expr))
#define DO(expr) \
__builtin_choose_expr(__type_is_void(expr), \
__DO_VOID(expr), \
__DO(__expr_or_zero(expr))) # works fine!
那就是它!以下是Ideone的完整源代码:http://ideone.com/EFy4pE
答案 1 :(得分:2)
你能接受“这不太可能”的答案吗?
不是关于从宏返回的部分。但是关于有条件地测试expr的返回类型的部分。
实际上,您要求的内容如下:
让我们说而不是一些名为“is_expr_type_void(expr)”的神奇检查,而是简单地在调用时传递1或0来表示宏的以下变体中的is_void或!is_void:
#define TIMER(is_void, expr, fmt_msg, ...) \
({ \
struct timeval before, after; \
uint64_t time_span; \
int time_span_sec, time_span_usec; \
gettimeofday(&before, NULL); \
if (is_void) \
(expr) \
else \
typeof(expr) _timer_expr__ = (expr); \ // <- static if?
gettimeofday(&after, NULL); \
time_span = (after.tv_sec * 1000000 + after.tv_usec) \
- (before.tv_sec * 1000000 + before.tv_usec); \
time_span_sec = time_span / 1000000; \
time_span_usec = time_span % 1000000; \
TRACE(fmt_msg "\n%s : %d.%d seconds", \
#expr, time_span_sec, time_span_usec, ...); \
if (!is_void) \
_timer_expr__; \
})
这根本行不通。在所有情况下,预处理器都会为if-else条件创建代码,包括void和non-void函数调用。并且双方都会为非空函数编译。但是当使用void函数调用TIMER时,编译器总是会阻塞条件的“else”部分......尽管代码永远不会被调用。
(现在如果存在一个非常聪明的编译器,它既可以识别死代码,也可以在将其标记为编译时错误之前将其删除,那么你很幸运!但我不认为gcc 4.6就是那么聪明......)
这将为您提供#define中#if(is_void)条件的首选选项。但这根本不允许。因为,this answer指出在尝试回答关于条件预处理的类似问题时,预处理器不是turing-complete。
所以...尽管你想拥有一个宏,但我认为你最简单的答案是为void函数创建一个,而为返回值的函数创建一个。
答案 2 :(得分:0)
如果您确实需要从宏返回,请改用内嵌功能。
答案 3 :(得分:0)
只要您拥有typeof
和_Generic
,也可以不用__builtin_types_compatible_p
或__builtin_choose_expr
来做到这一点。
需要注意的是,_Generic
不会让您与void
匹配,因此与其将Expr
与void
匹配,不如将(typeof(Expr)*){0}
与{{1} }。
下面的eldar-abusalimov示例已修改为使用void*
而不是_Generic
和__builtin_types_compatible_p
:
__builtin_choose_expr