Understanding assert code

S

Sami

Hi

I am using some C code with the following macro:

#define __force_assert(x) ((assert)(x))

Can anyone explain what is going on here.

Thanks.
 
B

Ben Pfaff

Sami said:
I am using some C code with the following macro:

#define __force_assert(x) ((assert)(x))

This will invoke a function named assert, ignoring any
function-like macro of the same name.

That's puzzling. The C standard says that assert is a macro. It
doesn't say anything about an assert function. I don't know
anyone would want to do this.
 
S

Seebs

Hi

I am using some C code with the following macro:

#define __force_assert(x) ((assert)(x))

Can anyone explain what is going on here.

Someone is trying to be clever.

assert(x) aborts if x is untrue... Sometimes.

If you #define the symbol NDEBUG before including <assert.h>,
assert(x) is a no-op.

Now, since this is happening in the preprocessor, Our Protagonist
has concluded that obviously, this is done by re-defining the
name "assert", which is presumably a function-like macro. (He's
probably right; in particular, it can't be a non-function-like macro,
I don't think, because so far as I can tell you're allowed to have
a variable named that, though it might be unwise.)

So he's using (assert) to try to force the compiler to generate
an actual call to the assert() function rather than macroing it
away. This might work, because function-like-macros are only
expanded when the next thing after the macro name is a left
parens. Instead, he's arranged for it to be a right parens.

I would never do this.

-s
 
K

Keith Thompson

Ben Pfaff said:
This will invoke a function named assert, ignoring any
function-like macro of the same name.

That's puzzling. The C standard says that assert is a macro. It
doesn't say anything about an assert function. I don't know
anyone would want to do this.

I suppose someone (not the C implementation) might provide a function
called "assert", but that would be a silly thing to do.
 
K

Keith Thompson

Kenneth Brody said:
Well, the assert() macro becomes a no-op if NDEBUG is #defined. It appears
that this implementation has created __force_assert() which remains in
effect even in the presence of NDEBUG.

The OP didn't say that the implementation created it.

If it is part of the implementation, it might actually be reasonable;
it could be a way to provide something that acts like the standard
assert() macro but that isn't affected by NDEBUG.

If it's not part of the implementation, then the use of a reserved
name starting with "__" is ill-advised, as is the assumption that
there's an actual function called "assert".

Also, an actual function can't produce a message containing the text
of the argument and the values of __FILE__, __LINE__, and __func__.

Sami, where did you see this? Is it part of a program, or is it
in a header provided by the implementation?
 
S

Sami

Keith said:
The OP didn't say that the implementation created it.

If it is part of the implementation, it might actually be reasonable; it
could be a way to provide something that acts like the standard assert()
macro but that isn't affected by NDEBUG.

If it's not part of the implementation, then the use of a reserved name
starting with "__" is ill-advised, as is the assumption that there's an
actual function called "assert".

Also, an actual function can't produce a message containing the text of
the argument and the values of __FILE__, __LINE__, and __func__.

Sami, where did you see this? Is it part of a program, or is it in a
header provided by the implementation?

Hi Kieth,

The codebase I'm working on has a couple of executables and a shared DLL.
One of the headers in this DLL provides __force_assert.

An example of how it's used in the code is:

char * fname_new = strdup(fname);
__force_assert( fname_new != NULL );

Most functions and macros in the DLL have a __ prefix, I guess to avoid
polluting the executable's "namespace".
 
K

Keith Thompson

Sami said:
Keith Thompson writes: [...]
I am using some C code with the following macro:

#define __force_assert(x) ((assert)(x))
[...]
Sami, where did you see this? Is it part of a program, or is it in a
header provided by the implementation?

Hi Kieth,

It's "Keith".
The codebase I'm working on has a couple of executables and a shared DLL.
One of the headers in this DLL provides __force_assert.

An example of how it's used in the code is:

char * fname_new = strdup(fname);
__force_assert( fname_new != NULL );

Where is the "assert" function declared or defined? The standard C
header <assert.h> defines a *macro* called "assert"; it doesn't declare
a function by that name. On the implementation I use, there is no
assert() function; this:
#include <assert.h>
...
assert(0);
fails to compile.
Most functions and macros in the DLL have a __ prefix, I guess to avoid
polluting the executable's "namespace".

That's a bad idea. Rather than polluting the executable's namespace,
it's polluting the implementation's namespace.
 
B

Ben Pfaff

Keith Thompson said:
Where is the "assert" function declared or defined? The standard C
header <assert.h> defines a *macro* called "assert"; it doesn't declare
a function by that name. On the implementation I use, there is no
assert() function; this:
#include <assert.h>
...
assert(0);
fails to compile.

Why? (I'm assuming that you actually put that statement inside a
function body.)
 
K

Keith Thompson

Ben Pfaff said:
Why? (I'm assuming that you actually put that statement inside a
function body.)

Because, rather than following the advice I give several times a
week to copy-and-paste actual code rather than paraphrasing it by
re-typing it, I paraphrased the actual code by re-typing it and
got it wrong in a way that destroyed the point I was trying to make.

Here's the actual code:

#include <assert.h>
int main(void) {
(assert)(0);
return 0;
}

And here are the error messages from the compiler:

c.c: In function ‘main’:
c.c:3: error: ‘assert’ undeclared (first use in this function)
c.c:3: error: (Each undeclared identifier is reported only once
c.c:3: error: for each function it appears in.)
 
E

Eric Sosman

Because, rather than following the advice I give several times a
week to copy-and-paste actual code rather than paraphrasing it by
re-typing it, I paraphrased the actual code by re-typing it and
got it wrong in a way that destroyed the point I was trying to make.

Here's the actual code:

#include<assert.h>
int main(void) {
(assert)(0);
return 0;
}

And here are the error messages from the compiler:

c.c: In function ‘main’:
c.c:3: error: ‘assert’ undeclared (first use in this function)
c.c:3: error: (Each undeclared identifier is reported only once
c.c:3: error: for each function it appears in.)

Interestingly, the diagnostic is not required. Quoth the
Standard: "If the macro definition is suppressed in order to access
an actual function, the behavior is undefined," and "undefined"
includes the possibility that no diagnostic appears, that the code
compiles, and that any oddities happen at run-time.

It seems to me that the authors of the macro (reproduced here
for reference):

#define __force_assert(x) ((assert)(x))

.... were trying to be responsible citizens, but didn't quite think
things through. If an implementation's assert() macro actually
generates a call on an assert() helper function (which it may), this
__force_assert() macro necessarily generates an incorrect call. (Of
course, the assert() implementation might be magical -- but in that
case, the __force_assert() kludge is on even thinner ice!)
 
S

Sami

Keith said:
Sami said:
Keith Thompson writes: [...]
I am using some C code with the following macro:

#define __force_assert(x) ((assert)(x)) [...]
Sami, where did you see this? Is it part of a program, or is it in a
header provided by the implementation?

Hi Kieth,

It's "Keith".
The codebase I'm working on has a couple of executables and a shared
DLL. One of the headers in this DLL provides __force_assert.

An example of how it's used in the code is:

char * fname_new = strdup(fname);
__force_assert( fname_new != NULL );

Where is the "assert" function declared or defined? The standard C
header <assert.h> defines a *macro* called "assert"; it doesn't declare
a function by that name. On the implementation I use, there is no
assert() function; this:
#include <assert.h>
...
assert(0);
fails to compile.
Most functions and macros in the DLL have a __ prefix, I guess to avoid
polluting the executable's "namespace".

That's a bad idea. Rather than polluting the executable's namespace,
it's polluting the implementation's namespace.

Hi

Could you explain what you mean by "the implementation" in this context?

You may be aware that C only has one global namespace - the only
exception is variables with static storage which have symbols only tied
to a single object file.
 
S

Seebs

Could you explain what you mean by "the implementation" in this context?

The implementation is the C compiler and its support code -- the
name seems self-explanatory. It is the implementation of C you're
using.
You may be aware that C only has one global namespace - the only
exception is variables with static storage which have symbols only tied
to a single object file.

Yes. However, some names are reserved for the implementation to use.
It is always undefined behavior for a program to use those names, even
if the implementation happens not to be using them yet. This is because
implementations often need additional helper functions or macros or
whatnot, and we don't want those names to break user programs.

So those names are reserved for the implementation, and when the "clever"
programmer uses __force_assert, he's stepping into a general category
of names (two leading underscores) which is reserved for someone else.

In short, yes, C has only one "global namespace" -- but it is useful
to distinguish between "the set of all names reserved for the
implementation and prohibited to developers" and "the set of all names
reserved for software developers and prohibited to the implementation".
We tend to refer to the latter as "the implementation's namespace".

-s
 
M

Marcin Grzegorczyk

Seebs said:
In short, yes, C has only one "global namespace" -- but it is useful
to distinguish between "the set of all names reserved for the
implementation and prohibited to developers" and "the set of all names
reserved for software developers and prohibited to the implementation".
We tend to refer to the latter as "the implementation's namespace".

The former, not the latter.
 
S

Seebs

The former, not the latter.

This kind of attention to detail is why I *love* code review.

I am very good at getting the hard parts right, but the easy parts
mystify me.

-s
 
T

Tim Rentsch

Eric Sosman said:
Because, rather than following the advice I give several times a
week to copy-and-paste actual code rather than paraphrasing it by
re-typing it, I paraphrased the actual code by re-typing it and
got it wrong in a way that destroyed the point I was trying to make.

Here's the actual code:

#include<assert.h>
int main(void) {
(assert)(0);
return 0;
}

And here are the error messages from the compiler:

c.c: In function 'main':
c.c:3: error: 'assert' undeclared (first use in this function)
c.c:3: error: (Each undeclared identifier is reported only once
c.c:3: error: for each function it appears in.)

Interestingly, the diagnostic is not required. Quoth the
Standard: "If the macro definition is suppressed in order to access
an actual function, the behavior is undefined," and "undefined"
includes the possibility that no diagnostic appears, that the code
compiles, and that any oddities happen at run-time. [snip]

I'm not sure if you're meaning to make a subtle point or if
you've just overlooked something. If the identifier 'assert' is
not declared (or defined as an enumeration constant), then in
fact a diagnostic must be issued. The Standard is quite clear
about this, in 5.1.1.3p1, when it says a diagnostic must be
produced whenever there is

a violation of any syntax rule or constraint, even if
the behavior is also explicitly specified as undefined
or implementation-defined.

If 'assert' hasn't been declared (or defined as an enumeration
constant), then it doesn't match the syntax for primary-expression,
which requires a diagnostic, even if there is undefined behavior.

The subtlety is whether <assert.h> is allowed to declare 'assert'
as a regular identifier (or define it as an enumeration constant).
My impression is that it is not. For example, consider

enum blah { assert = 0 };
#include <assert.h>
int main(){ return assert; }

I believe this program is supposed to be strictly conforming.

Another possibility: could 'assert' be defined as an object-like
macro rather than a function-like macro? Again my impression is
that it cannot. Another program:

struct { int assert; } x = 0;
#include <assert.h>
int main(){ return x.assert; }

Here again I believe this program is supposed to be strictly
conforming. More specifically, for both programs I'm not
aware of any restriction that has been violated.

The weak point of the above comments is that the Standard never
says specifically (at least not as far as I'm aware) that the
definition for 'assert' must be as a function-like macro and not as
an object-like macro (assuming NDEBUG is not defined). However,
all the evidence I'm aware of makes the 'must be a function-like
macro' choice appear to be the more reasonable one.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top