GCC __attribute__((pure))
和__attribute__((const))
允许函数分别声明为非副作用和引用透明;假设我想编写pure_assert
和const_assert
宏,其参数必须是适当严格级别的表达式,即:
assert(oops_a_side_effect());
在调试和发布中静默导致不同的行为,但是:
pure_assert(oops_a_side_effect());
const_assert(oops_read_a_global());
至少在调试版本中会出现编译时错误。对于我希望显而易见的原因,您不能只创建一个pure_assert_impl
声明__attribute__((pure))
并将宏扩展到它。那么可以编写这些宏吗?
答案 0 :(得分:2)
gcc是否强制 pure 和 const 函数分别不能调用非纯函数或非const函数?如果是这样,您可以定义一个正确归因的函数模板,该函数模板将函数指针作为模板参数,并让宏扩展为该函数模板的调用。我假设您需要支持带参数的函数,这样可以更好地使用C ++ 0x可变参数模板或lambdas。
答案 1 :(得分:2)
gcc不以任何方式强制执行纯度或引用透明度。这些属性只是优化器的提示。所以答案是否定的。
答案 2 :(得分:1)
我怀疑是否有一个真正的良好的解决方案,但我发现像const_assert()
这样的东西,出现的可能性与我有两个版本的gcc(4.1.2和4.4.4)。
#include <assert.h>
extern void dummy_void_function(void);
#define const_assert(x) \
assert(__builtin_choose_expr(__builtin_constant_p((x) == (x)), \
(x), dummy_void_function()))
extern int function(void);
extern int __attribute__((pure)) pure_function(void);
extern int __attribute__((const)) const_function(void);
extern int global;
extern volatile int volatile_global;
void test(int arg)
{
/* These cause compile-time errors: */
const_assert(function() == 0);
const_assert(pure_function() == 0);
const_assert(volatile_global == 0);
/* These don't: */
const_assert(const_function() == 0);
const_assert(arg == 0);
const_assert(global == 0);
}
这实际上是检查表达式(x) == (x)
是否被编译器视为编译时常量,如果不是则创建断点。 “好”案例实际上变成了assert(x);
而不好的案例会产生编译时错误:
$ gcc -c const_assert.c
const_assert.c: In function 'test':
const_assert.c:18: error: void value not ignored as it ought to be
const_assert.c:19: error: void value not ignored as it ought to be
const_assert.c:20: error: void value not ignored as it ought to be
但是,启用优化后,它仍会在预期的情况下产生错误,但其中一个有点奇怪:
$ gcc -O -c const_assert.c
const_assert.c: In function 'test':
const_assert.c:18: error: void value not ignored as it ought to be
const_assert.c:19: error: first argument to '__builtin_choose_expr' not a constant
const_assert.c:20: error: void value not ignored as it ought to be
...根据定义,你期望__builtin_constant_p()
的结果被视为常数!所以我不确定我真的会相信真正的代码......
(我现在对pure_assert()
没有任何好的想法!)
答案 3 :(得分:0)
运气好,我很害怕。即使在编译器开始查看代码之前,宏也会被预处理器扩展。
可能可能是一个解决方案,就是无论发布模式还是调试模式都要评估断言测试表达式,但是只能在调试模式下测试结果。