4

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?

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • I think the person who invented the term "referential transparency" should be punished in some way. I had to look it up, and Wikipedia states that when a function is referential transparent, then you can replace a call with its function result. Still not clear what the difference is from a function without side effects? – Cheers and hth. - Alf Jun 27 '11 at 10:37
  • 2
    A true random number generator (i.e. hardware based) would have no side effects (calling it doesn't change the program state; all future calls remain truely random) but it's certainly not referentially transparent (can't replace function by its result, as it wouldn't be random anymore). "no side effects" is also known as a "pure function". As WP notes, any pure function or combination thereof is referentially transparent. – MSalters Jun 27 '11 at 12:48

4 Answers4

2

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.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2

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

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
1

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()!)

Matthew Slattery
  • 45,290
  • 8
  • 103
  • 119
0

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.

Sqenqe
  • 84
  • 3