Is there a version of assert() that doesn't exit?

S

Steve555

Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?

I found this in the header; I would try and write my own if I could
decipher it.

#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)

Is it the #e or (void)0 that means exit? And where does
__builtin_expect come from?

Thanks

Steve
 
L

Lew Pitcher

Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?

I found this in the header;

"Reading" compiler headers is sometimes a tricky business. Often, the
headers contain /compiler specific/ values, rather than generic "Standard
C" code.
I would try and write my own if I could
decipher it.

#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)

Remember that "compiler specific" possibility in headers? You've run into it
here. __builtin_expect() is a function or macro specific to your Gnu C
compiler, as is __assert_rtn(). The compiler documentation /may/ document
the purpose, operation, and/or proper invocation of either of these names,
but don't count on it.

And, both of those functions/macros are /essential/ to the workings of the
GCC implementation of the assert() macro. In fact, all the details of how
the GCC assert() macro works are buried within those two functions/macros.
Is it the #e or (void)0 that means exit?

No. Neither "mean" exit.

#e converts e into a string, so that, given
assert(10+1)
the results would include the string "10+1"

(void)0 provides a default NO-OP action for the GCC assert() macro

And where does __builtin_expect come from?

From within the GCC compiletime macros or runtime functions, along with the
__assert_rtn().

FWIW, __assert_rtn() does the actual work of printing out the assertion, and
exiting the program.
 
E

Edward

Steve555 said:
Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?

I found this in the header; I would try and write my own if I could
decipher it.

#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)

Is it the #e or (void)0 that means exit? And where does
__builtin_expect come from?

Thanks

Steve
The (void)0 is returned when the assertion succeeds (because, clearly,
__assert_rtn is called when it fails); AFAICT it's simply a meaningless
return value (void because you shouldn't be using the r.v. of assert()).
#e is the "stringification"[1] of e, i.e. it expands to the text of e,
since you want the 'Assert failed...' message to tell you /what/ was
being asserted.
I don't know what __builtin_expect does, but __assert_rtn typically
calls abort(). If you want a 'non-fatal' assertion, I'd suggest
#define s_assert(e) \
(__builtin_expect(!(e), 0) ? fprintf(stderr, "Soft assertion failed, %s
at %s:%s in %s\n", #e, __FILE__, __LINE__, __func__) : (void)0)
but with the following caveats:
* I don't know why __builtin_expect is there in the first place or
whether it's appropriate to keep it.
* fprintf is not async-signal-safe, so don't use this s_assert() in a
signal handler.

Better still, write proper error handling code around everything with
descriptive, meaningful error messages. If the descriptive and
meaningful bit is too much work, another possibility is
#define s_assert(e) \
(__builtin_expect(!(e), 0) ? fprintf(stderr, "Soft assertion failed, %s
at %s:%s in %s\n", #e, __FILE__, __LINE__, __func__), false : true)
which you can use as
if(s_assert(foo))
// do normal stuff
else
// handle the error
Which solution is appropriate will, of course, depend on your individual
requirements (eg. are these asserts purely for debugging, or will they
be retained in production code? The latter is bad practice for asserts
but may not be for soft asserts).

-Edward

Refs:
[1] http://gcc.gnu.org/onlinedocs/cpp/Stringification.html
 
E

Eric Sosman

Is there a standard function or macro that does the same as assert()
(printing the file& line info), but returning without exiting?

No. However, assert() exits "as if" by raise(SIGABRT), and you
and you can set up a signal handler for SIGABRT that just returns
instead of halting the program.

(It's not clear *why* you'd want to continue after an assert()
failure, and it suggests to me that you may be using assert() for the
wrong sort of thing. It's specifically designed to halt the program
when a programming error is detected, that is, when the program has
proven that it is malfunctioning and can't be relied upon. It' not an
appropriate mechanism for handling input errors and the like: If a
user enters a negative number when he's supposed to supply positives,
assert() is not the way to handle the situation.)
I found this in the header; I would try and write my own if I could
decipher it.

#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)

Is it the #e or (void)0 that means exit? And where does
__builtin_expect come from?

To the first question, "Neither." The #e produces the argument
expression e in the form of a string, so `assert(count > 0)' can
actually include "count > 0" in the output if the test fails. As
for the (void)0, it's to give type void to the "value" of the entire
assert() expression, preventing you from making silly errors like

x = assert(count > 0);

(Other silly errors like `assert(--count > 0)' remain unprevented.)

To the second question, __builtin_expect() is presumably some
kind of compiler magic, something the compiler that processes this
header will understand. That is, it's a part of this compiler's
implementation of assert(), just as __assert_rtn() is, and not part
of the interface; other compilers will do it differently and may
not have such a thing at all. As a user of assert(), you are not
expected to understand or rely on the internals of its implementation,
just as you are not expected to understand or rely on the internals
of the printf() implementation. Your compiler's documentation may
provide more information on these "private" matters, but unless you
are actually hacking on the implementation itself you'd be well
advised to ignore it.
 
J

Jens Thoms Toerring

Steve555 said:
Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?
I found this in the header; I would try and write my own if I could
decipher it.
#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)
Is it the #e or (void)0 that means exit? And where does
__builtin_expect come from?

The __builtin_expect() is probably from somewhere deep in the
bowels of the standard library of your system. And the exit()
(or probably rather a call of abort()) is mostly to happens
in __assert_rtn().

Here's a version of assert I once wrote for my own amusement:

------- my_assert.h -----------------------------------

#if ! defined MY_ASSERT_HEADER
#define MY_ASSERT_HEADER

int my_assert_print( const char * /* expression */,
const char * /* filename */,
int /* line */ );

#ifdef NDEBUG
#define my_assert( expression ) do { } while ( 0 )
#else
#define my_assert( expression ) \
( ( void ) ( ( expression ) ? \
0 : my_assert_print( #expression, __FILE__, __LINE__ ) ) )
#endif

#endif /* ! MY_ASSERT__HEADER */

----------------------------------------------------

and

------- my_assert.c --------------------------------

#include "my_assert.h"

int
my_assert_print( const char * expression,
const char * filename,
int line )
{
fprintf( stderr, "%s:%d: failed assertion: %s\n", filename, line,
expression );
abort( );
return 0;
}

----------------------------------------------------

If you get rid of the call of abort() in the my_assert_print()
function it should just do what you're looking for (but note
that this is untested, i.e. I removed a number of bits irrele-
vant for you, but that may have broken something!).

Regards, Jens
 
E

Edward

Edward said:
Steve555 said:
Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?

I found this in the header; I would try and write my own if I could
decipher it.

#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)

Is it the #e or (void)0 that means exit? And where does
__builtin_expect come from?

Thanks

Steve
The (void)0 is returned when the assertion succeeds (because, clearly,
__assert_rtn is called when it fails); AFAICT it's simply a meaningless
return value (void because you shouldn't be using the r.v. of assert()).
#e is the "stringification"[1] of e, i.e. it expands to the text of e,
since you want the 'Assert failed...' message to tell you /what/ was
being asserted.
I don't know what __builtin_expect does, but __assert_rtn typically
calls abort(). If you want a 'non-fatal' assertion, I'd suggest
#define s_assert(e) \
(__builtin_expect(!(e), 0) ? fprintf(stderr, "Soft assertion failed,
%s at %s:%s in %s\n", #e, __FILE__, __LINE__, __func__) : (void)0)
but with the following caveats:
* I don't know why __builtin_expect is there in the first place or
whether it's appropriate to keep it.
* fprintf is not async-signal-safe, so don't use this s_assert() in a
signal handler.

Better still, write proper error handling code around everything with
descriptive, meaningful error messages. If the descriptive and
meaningful bit is too much work, another possibility is
#define s_assert(e) \
(__builtin_expect(!(e), 0) ? fprintf(stderr, "Soft assertion failed,
%s at %s:%s in %s\n", #e, __FILE__, __LINE__, __func__), false : true)
which you can use as
if(s_assert(foo))
// do normal stuff
else
// handle the error
Which solution is appropriate will, of course, depend on your individual
requirements (eg. are these asserts purely for debugging, or will they
be retained in production code? The latter is bad practice for asserts
but may not be for soft asserts).

-Edward

Refs:
[1] http://gcc.gnu.org/onlinedocs/cpp/Stringification.html
Having just looked up __builtin_expect, it turns out it's not (as Lew
Pitcher says) essential to assert()'s operation; instead it "provide
the compiler with branch prediction information". In other words, when
you say __builtin_expect(e, c), you're telling the compiler "I want to
use the value of e in a conditional, but it'll almost always be c, so
make the case e==c cheap at the expense of e!=c" (which maybe means
choosing short instead of long jumps, or something like that). In GCC,
e and c are longs.

In other words, it's an optimisation.

-Edward

Ref:
http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-g_t_005f_005fbuiltin_005fexpect-3096
 
S

Steve555

"Reading" compiler headers is sometimes a tricky business. Often, the
headers contain /compiler specific/ values, rather than generic "Standard
C" code.



Remember that "compiler specific" possibility in headers? You've run into it
here.  __builtin_expect() is a function or macro specific to your Gnu C
compiler, as is __assert_rtn(). The compiler documentation /may/ document
the purpose, operation, and/or proper invocation of either of these names,
but don't count on it.

And, both of those functions/macros are /essential/ to the workings of the
GCC implementation of the assert() macro. In fact, all the details of how
the GCC assert() macro works are buried within those two functions/macros..


No. Neither "mean" exit.

#e converts e into a string, so that, given
  assert(10+1)
the results would include the string "10+1"

(void)0 provides a default NO-OP action for the GCC assert() macro


From within the GCC compiletime macros or runtime functions, along with the
__assert_rtn().

FWIW, __assert_rtn() does the actual work of printing out the assertion, and
exiting the program.

--
Lew Pitcher
Master Codewright & JOAT-in-training   | Registered Linux User #112576
Me:http://pitcher.digitalfreehold.ca/| Just Linux:http://justlinux.ca/
----------      Slackware - Because I know what I'm doing.         ------

Thanks Lew. I'm glad to hear it's compiler-specific magic, because I
wondered why after studying C for some time, that this sounded like a
foreign language. I guess I should have come across #e before though.

Steve
 
S

Steve555

Steve555 said:
Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?
I found this in the header; I would try and write my own  if I could
decipher it.
#define    assert(e) \
    (__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)
Is it the  #e  or   (void)0  that means exit?  And where does
__builtin_expect come from?

Steve

The (void)0 is returned when the assertion succeeds (because, clearly,
__assert_rtn is called when it fails); AFAICT it's simply a meaningless
return value (void because you shouldn't be using the r.v. of assert()).
#e is the "stringification"[1] of e, i.e. it expands to the text of e,
since you want the 'Assert failed...' message to tell you /what/ was
being asserted.
I don't know what __builtin_expect does, but __assert_rtn typically
calls abort().  If you want a 'non-fatal' assertion, I'd suggest
#define         s_assert(e) \
        (__builtin_expect(!(e), 0) ? fprintf(stderr, "Soft assertion failed, %s
at %s:%s in %s\n", #e, __FILE__, __LINE__, __func__) : (void)0)
but with the following caveats:
* I don't know why __builtin_expect is there in the first place or
whether it's appropriate to keep it.
* fprintf is not async-signal-safe, so don't use this s_assert() in a
signal handler.

Better still, write proper error handling code around everything with
descriptive, meaningful error messages.  If the descriptive and
meaningful bit is too much work, another possibility is
#define         s_assert(e) \
        (__builtin_expect(!(e), 0) ? fprintf(stderr, "Soft assertion failed, %s
at %s:%s in %s\n", #e, __FILE__, __LINE__, __func__), false : true)
which you can use as
        if(s_assert(foo))
                // do normal stuff
        else
                // handle the error
Which solution is appropriate will, of course, depend on your individual
requirements (eg. are these asserts purely for debugging, or will they
be retained in production code?  The latter is bad practice for asserts
but may not be for soft asserts).

-Edward

Refs:
[1]http://gcc.gnu.org/onlinedocs/cpp/Stringification.html

Thanks for the examples, and yes, at the moment it's just for
debugging.
I was trying to write good descriptive error handling, but wanted to
make use of the file, line and func descriptions too, which is when I
thought about adapting assert().
Thanks for the Stringification link... you learn something every day!
 
L

Lew Pitcher

Edward said:
Steve555 said:
Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?

I found this in the header; I would try and write my own if I could
decipher it.

#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)
Refs:
[1] http://gcc.gnu.org/onlinedocs/cpp/Stringification.html
Having just looked up __builtin_expect, it turns out it's not (as Lew
Pitcher says) essential to assert()'s operation;

Hmmmm.
Question: In the assert() macro above, what component evaluates the
assertion?

Answer: The invocation of __builtin_expect(), and /only that invocation/
evaluates the assertion.

Question: What would happen if we removed the invocation of
__builtin_expect() from the macro above?

Answer: Without replacement with some other text that evaluates the
assertion, the macro would expand to an invalid statement.

Question: Would expansion to an invalid statement be an acceptable
implementation of assert()?

Answer: No.

Assertion: Then, /in the macro above/, the invocation of __builtin_expect()
is essential to the operation of the assert() macro /above/.

Your rebuttal, please :)
instead
s/instead/also/

it "provide the compiler with branch prediction information".

In other words, when
you say __builtin_expect(e, c), you're telling the compiler "I want to
use the value of e in a conditional, but it'll almost always be c, so
make the case e==c cheap at the expense of e!=c" (which maybe means
choosing short instead of long jumps, or something like that). In GCC,
e and c are longs.

In other words, it's an optimisation.


that, as a side effect, evaluates the expression being asserted, and /as a
direct result of the return value of __builtin_expect()/ invokes either
__assert_rtn() or (void)0.

HTH
 
S

Steve555

The __builtin_expect() is probably from somewhere deep in the
bowels of the standard library of your system. And the exit()
(or probably rather a call of abort()) is mostly to happens
in __assert_rtn().

Here's a version of assert I once wrote for my own amusement:

------- my_assert.h -----------------------------------

#if ! defined MY_ASSERT_HEADER
#define MY_ASSERT_HEADER

int my_assert_print( const char * /* expression */,
                     const char * /* filename   */,
                     int          /* line       */  );

#ifdef NDEBUG
#define my_assert( expression ) do { } while ( 0 )
#else
#define my_assert( expression )                                     \
       ( ( void ) ( ( expression ) ?                                \
       0 : my_assert_print( #expression, __FILE__, __LINE__ ) ) )
#endif

#endif   /* ! MY_ASSERT__HEADER */

----------------------------------------------------

and

------- my_assert.c --------------------------------

#include "my_assert.h"

int
my_assert_print( const char * expression,
                 const char * filename,
                 int          line )
{
    fprintf( stderr, "%s:%d: failed assertion: %s\n", filename, line,
             expression );
    abort( );
    return 0;

}

----------------------------------------------------

If you get rid of the call of abort() in the my_assert_print()
function it should just do what you're looking for (but note
that this is untested, i.e. I removed a number of bits irrele-
vant for you, but that may have broken something!).

                              Regards, Jens

Hey thanks Jens, I modified your my_assert, and it's just what I
needed :)
 
S

Steve555

     No.  However, assert() exits "as if" by raise(SIGABRT), and you
and you can set up a signal handler for SIGABRT that just returns
instead of halting the program.

     (It's not clear *why* you'd want to continue after an assert()
failure, and it suggests to me that you may be using assert() for the
wrong sort of thing.  It's specifically designed to halt the program
when a programming error is detected, that is, when the program has
proven that it is malfunctioning and can't be relied upon.  It' not an
appropriate mechanism for handling input errors and the like: If a
user enters a negative number when he's supposed to supply positives,
assert() is not the way to handle the situation.)




     To the first question, "Neither."  The #e produces the argument
expression e in the form of a string, so `assert(count > 0)' can
actually include "count > 0" in the output if the test fails.  As
for the (void)0, it's to give type void to the "value" of the entire
assert() expression, preventing you from making silly errors like

        x = assert(count > 0);

(Other silly errors like `assert(--count > 0)' remain unprevented.)

     To the second question, __builtin_expect() is presumably some
kind of compiler magic, something the compiler that processes this
header will understand.  That is, it's a part of this compiler's
implementation of assert(), just as __assert_rtn() is, and not part
of the interface; other compilers will do it differently and may
not have such a thing at all.  As a user of assert(), you are not
expected to understand or rely on the internals of its implementation,
just as you are not expected to understand or rely on the internals
of the printf() implementation.  Your compiler's documentation may
provide more information on these "private" matters, but unless you
are actually hacking on the implementation itself you'd be well
advised to ignore it.

Thanks for the explanation.

//It's not clear *why* you'd want to continue after an assert()
failure

Of course, I was trying to understand how these macros work so I could
write some better error handling.
 
R

robertwessel2

The __builtin_expect() is probably from somewhere deep in the
bowels of the standard library of your system. And the exit()
(or probably rather a call of abort()) is mostly to happens
in __assert_rtn().

Here's a version of assert I once wrote for my own amusement:

------- my_assert.h -----------------------------------

#if ! defined MY_ASSERT_HEADER
#define MY_ASSERT_HEADER

int my_assert_print( const char * /* expression */,
                     const char * /* filename   */,
                     int          /* line       */  );

#ifdef NDEBUG
#define my_assert( expression ) do { } while ( 0 )
#else
#define my_assert( expression )                                     \
       ( ( void ) ( ( expression ) ?                                \
       0 : my_assert_print( #expression, __FILE__, __LINE__ ) ) )
#endif

#endif   /* ! MY_ASSERT__HEADER */

----------------------------------------------------

and

------- my_assert.c --------------------------------

#include "my_assert.h"

int
my_assert_print( const char * expression,
                 const char * filename,
                 int          line )
{
    fprintf( stderr, "%s:%d: failed assertion: %s\n", filename, line,
             expression );
    abort( );
    return 0;

}

----------------------------------------------------

If you get rid of the call of abort() in the my_assert_print()
function it should just do what you're looking for (but note
that this is untested, i.e. I removed a number of bits irrele-
vant for you, but that may have broken something!).


This has a significant difference from standard assert. You cannot re-
include the assert.h header with a different setting of NDEBUG.
Assert.h is *not* idempotent. You need to recode the header something
like this (not tested, etc.):

------- my_assert.h -----------------------------------

#if ! defined MY_ASSERT_HEADER
#define MY_ASSERT_HEADER

int my_assert_print( const char * /* expression */,
const char * /* filename */,
int /* line */ );

#endif /* ! MY_ASSERT__HEADER */

#undef my_assert

#ifdef NDEBUG
#define my_assert( expression ) do { } while ( 0 )
#else
#define my_assert( expression ) \
( ( void ) ( ( expression ) ? \
0 : my_assert_print( #expression, __FILE__, __LINE__ ) ) )
#endif
 
C

Chïna Blüe Öyster Cult

Steve555 said:
Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?

I found this in the header; I would try and write my own if I could
decipher it.

__func__ - An auto char* which is the function name.
__FILE__ - A #define of a string which is current source file.
__LINE__ - A #define of an integer which is current source line.
#e the #define argument as a string.

Assume
#define zeta(eta) char *eta = #eta;
then zeta(omicron) would expand into
char *omicron = "omicron";

The ?: needs both true-value and false-value. (void)0 is the false-value which
means no information available--if the assertion is true, the value of the
assert is (void)0.

You can define your own assertion macro to do whatever you want:
#define whinge(predicate) \
((predicate) ? 1 : \
(fprintf(stderr,"Look what you've just done to " \
"my freshly waxed floors in the %s. I don't know " \
"why I even bother at " __FILE__ "[%d].\n" \
"And no, " #predicate " is not true. Why can't you " \
"ever tell the truth.\n", \
__func__, __LINE__), \
0))
Then whinge(expression) will return the truth value of expression and print a
message to stderr if it false.
 
S

Seebs

Hmmmm.
Question: In the assert() macro above, what component evaluates the
assertion?

Answer: The invocation of __builtin_expect(), and /only that invocation/
evaluates the assertion.

Question: What would happen if we removed the invocation of
__builtin_expect() from the macro above?

Answer: Without replacement with some other text that evaluates the
assertion, the macro would expand to an invalid statement.

Yes, but since !(e) would be a perfectly good replacement, I don't think
that's a problem.

I think the point being made was that you don't actually need to have or
implement __builtin_expect() to make that macro work -- the only difference
between "!(e)" and "__builtin_expect(!(e), 0)" is that one has branch
prediction hints in it the other doesn't.

-s
 
L

Lew Pitcher

The macro would work just fine if you replaced "__builtin_expect (!
(e), 0)" with "!(e)".

True, but we weren't discussing /replacing/ __builtin_expect().
We were discussing whether, in that macro, __builtin_expect() did something
essential for the operation of the macro.
It would also work just fine with "__builtin_expect (!(e), 1)" except
the compiler would now believe that asserts will almost always assert,
and therefore likely produce non-optimal code.

For that matter, I would be happy with
#define assert(e) \
( (!(e) ? __assert_rtn(__func__, __FILE__, __LINE__, #e) : (void)0)

but we weren't discussing /changes/ to the given macro. We were discussing
the macro /as it was presented/. Or so I thought.
 
L

lawrence.jones

Eric Sosman said:
No. However, assert() exits "as if" by raise(SIGABRT), and you
and you can set up a signal handler for SIGABRT that just returns
instead of halting the program.

Actually, assert() exits by calling abort(), which calls raise(SIGABRT).
But abort() terminates the program even if the raise() call doesn't, so
you can't prevent termination by ignoring SIGABRT or installing a
handler than returns, you have to install a handler than *doesn't*
return (typically by calling longjmp()).
 
J

Jens Thoms Toerring

This has a significant difference from standard assert. You cannot re-
include the assert.h header with a different setting of NDEBUG.
Assert.h is *not* idempotent. You need to recode the header something
like this (not tested, etc.):
#if ! defined MY_ASSERT_HEADER
#define MY_ASSERT_HEADER
int my_assert_print( const char * /* expression */,
const char * /* filename */,
int /* line */ );
#endif /* ! MY_ASSERT__HEADER */
#undef my_assert
#ifdef NDEBUG
#define my_assert( expression ) do { } while ( 0 )
#else
#define my_assert( expression ) \
( ( void ) ( ( expression ) ? \
0 : my_assert_print( #expression, __FILE__, __LINE__ ) ) )
#endif

Thank you, I hadn't thought about that.

Best regards, Jens
 
E

Eric Sosman

Actually, assert() exits by calling abort(), which calls raise(SIGABRT).
But abort() terminates the program even if the raise() call doesn't, so
you can't prevent termination by ignoring SIGABRT or installing a
handler than returns, you have to install a handler than *doesn't*
return (typically by calling longjmp()).

Thanks for the correction, and sorry for the misinformation.
 
E

Edward

Lew said:
Edward said:
Steve555 wrote:
Is there a standard function or macro that does the same as assert()
(printing the file & line info), but returning without exiting?

I found this in the header; I would try and write my own if I could
decipher it.

#define assert(e) \
(__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__,
__LINE__, #e) : (void)0)
Refs:
[1] http://gcc.gnu.org/onlinedocs/cpp/Stringification.html
Having just looked up __builtin_expect, it turns out it's not (as Lew
Pitcher says) essential to assert()'s operation;

Hmmmm.
Question: In the assert() macro above, what component evaluates the
assertion?

Answer: The invocation of __builtin_expect(), and /only that invocation/
evaluates the assertion.

Question: What would happen if we removed the invocation of
__builtin_expect() from the macro above?

Answer: Without replacement with some other text that evaluates the
assertion, the macro would expand to an invalid statement.

Question: Would expansion to an invalid statement be an acceptable
implementation of assert()?

Answer: No.

Assertion: Then, /in the macro above/, the invocation of __builtin_expect()
is essential to the operation of the assert() macro /above/.

Your rebuttal, please :)
Very well. My meaning was that /the function/ __builtin_expect() is not
essential to the operation of assert(). Of course, that /invocation/ is
essential to the operation of /that particular macro for assert()/. But
I meant merely that one could replace it with (!e) which does not use
__builtin_expect(). In other words, that which is provided by
__builtin_expect() is not essential to the operation of assert(). More
pertinently perhaps, the operation of that assert() can be understood
without knowing anything about __builtin_expect(), as evidenced by the
fact that I deduced its operation (in the first of my two posts) before
discovering anything about __builtin_expect(). This is because,
starting from the premise that the message would need to be printed
precisely if the assert failed (and that the __FILE__ etc. must be in
the branch of the conditional which is taken when the message is
printed), one can deduce which branch of the conditional is taken when
the assert fails, and hence the entire operation of that assert() macro
except for the purpose of the __builtin_expect() call. Perhaps another
way to put it is that I thought not of "__builtin_expect(!(e), 0)" as an
entity, but of "lambda e . __builtin_expect(!(e), 0)", notionally a
'wrapper' which has been placed around e. It was this wrapper which I
considered non-essential to the operation of assert().

I suspect, then, that the problem arose from slightly loose use of
terminology on my part. Only very slightly loose, it must be said
(there are several finely-distinguished interpretations of what I said,
but almost all yield correct statements); yet this is CLC, so perhaps I
should have known better. If a sufficiently pedantic observer can come
to an interpretation of one's words in which one has uttered a
falsehood, someone in CLC will do so. This is clearly a law of nature.

Rebuttish enough?

-Edward

PS: Rogatio isn't a very effective form of rhetoric. Maybe you should
try something else next time.
 
E

Eric Sosman

[...] You need to recode the header something
like this (not tested, etc.):

------- my_assert.h -----------------------------------

#if ! defined MY_ASSERT_HEADER
#define MY_ASSERT_HEADER

int my_assert_print( const char * /* expression */,
const char * /* filename */,
int /* line */ );

#endif /* ! MY_ASSERT__HEADER */

#undef my_assert

#ifdef NDEBUG
#define my_assert( expression ) do { } while ( 0 )
#else
#define my_assert( expression ) \
( ( void ) ( ( expression ) ? \
0 : my_assert_print( #expression, __FILE__, __LINE__ ) ) )
#endif

Note that this wouldn't work as a substitute for the Standard
assert() macro, which must expand to a void expression rather than
to a statement. For example, one can write

if ( assert(i < N), array != 42 ) ...

Under NDEBUG, the corresponding line using my_assert() wouldn't compile.
 

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

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,280
Latest member
BGBBrock56

Latest Threads

Top