假设我想要一个可以在任何类型上工作的c宏。
我使用gcc编译器(>=4.6),可以使用gnu99宏。
//code...
any_type_t *retVal = function_that_runs_very_long_time(a, b, &&c, **d, &e, *f);
//other code...
定时器宏的用法可以如下所示
//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...
所以定时器必须返回给定函数的值并打印其运行的持续时间。
返回类型为
void
的函数有问题。很明显,我可以有两个宏,比如timer_type和timer_void,但是我想使用任何返回值的一对一函数。
谢谢你的建议。
此计时器宏的编辑示例
#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__; \
})
最佳答案
真是个有趣的问题,大人!
经过几次实验,我找到了一种使用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
。
所以这里是我们如何检查“void
ness”。
#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
内在函数类似于编译时计算的“静态开关”。准备
我不在这里粘贴您的
TIMER
宏,但我假设它可以将其分为两个版本:一个用于voidexpr
另一个用于其他版本。下面是对表达式求值并产生相同类型结果的存根。#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值替换原来的void
expr
,以便能够编译__DO
案例(当expr
为void时,这仍然是一个死代码)。#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