开发者

Is it possible to write these pure_assert and const_assert macros?

开发者 https://www.devze.com 2023-03-15 10:37 出处:网络
The GCC __attribute__((pure)) and __attribute__((const)) allow functions to be declared as non–side-effecting and referentially transparent, respectively; let\'s say I want to write pure_assert

The GCC __attribute__((pure)) and __attribute__((const)) allow functions to be declared as non–side-effecting and referentially transparent, respectively; let's say I want to write pure_assert and const_assert macros, whose argument must be an expression of the appropriate level of strictness, that is:

开发者_开发知识库
assert(oops_a_side_effect());

Silently results in different behaviours in debug and release, but:

pure_assert(oops_a_side_effect());
const_assert(oops_read_a_global());

Would be a compile-time error, at least in debug builds. For what I should hope are obvious reasons, you can't just create a pure_assert_impl declared __attribute__((pure)) and have the macro expand to it. So is it possible to write these macros?


Does gcc enforce that pure and const functions cannot call non-pure or non-const functions, respectively? If so, you could define a properly attributed function template which takes a function pointer as a template parameter, and let the macro expand to an invocation of that function template. I assume you need to support functions that take parameters, this would work better with C++0x variadic templates or lambdas.


gcc does not enforce purity or referential transparency in any way. These attributes are just hints for the optimiser. So the answer is no.


I doubt that there's a really good solution for this, but I've found a possibility for something like const_assert(), which appears to work with the two versions of gcc I have readily to hand (4.1.2 and 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);
}

This is really checking whether the expression (x) == (x) is regarded as a compile-time constant by the compiler, and creating brokenness if it's not. The "good" cases effectively become assert(x); and the bad ones generate compile-time errors:

$ 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

However, with optimisation enabled, it still produces errors in the expected cases, but one of them is a bit odd:

$ 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

...you'd expect the result of __builtin_constant_p() to be regarded as a constant by definition! So I'm not sure that I would really trust this for real code...

(And I don't have any good ideas right now for pure_assert()!)


Tough luck, I'm afraid. Macros are expanded by the preprocessor, even before the compiler starts looking at the code.

What maybe could be a solution, is to let the assert test expression be evaluated regardless of release or debug mode, however let the result be tested only in debug mode.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号