Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Verify and expression

15 views
Skip to first unread message

BinglongX

unread,
Oct 13, 2006, 8:57:47 AM10/13/06
to
I have used (Microsoft) MFC a while and found its macro VERIFY quite
useful. VERIFY(exp) is the same as ASSERT(exp) in debug compilation;
the only difference is that in non-debug compilation it still evaluates
exp. The problem is that I want to use it without MFC.

If I do not want to depend on MFC, there is assert(exp) but I could not
find verify(exp) or similar things in standard C/C++. So I just define
it myself:

#include <assert.h>
#ifdef NDEBUG
#define verify(exp) (exp)
#else
#define verify(exp) ( (exp) || (assert(0),0) )
#endif

Its direct usage is like this:
verify( start_bus() ); // assert(0) if start_bus() fails

This definition is different from MFC's, because the macro actually
"returns" the evaluated expression, so that I can write code like this:
bool ok = verify( start_bus() );
ok will have the return value of start_bus(), no matter in debug or
non-debug version. (in non-debug version, only when the assert handler
can resume the running, of course.)
In my code, I had something like this:
bool ok = check_function_args(...);
if( !ok )
{
assert(false); // alert for programming error.
return false; // do not go beyond in runtime as well.
}
And I can now replace it with much more short one, without a temporary
auto variable:
if( !verify( check_function_args() ) )
return false;

My question is, is there any pitfall in this macro definition? I assume
if this is really useful there would have been it somewhere?


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Greg Herlihy

unread,
Oct 13, 2006, 3:20:06 PM10/13/06
to

There are always pitfalls to using a macro - name collisions for
example. After all, "verify" is not that uncommon a word (especially in
programming). So it's easy to anticipate that the macro name "verify"
could very well end up conflicting with a "verify" identifier already
in use in some program.

Since the C++ way is to use inline functions instead of macros wherever
practical, why not just declare the non-debug verify() like so:

inline bool verify(bool inFlag) { return inFlag; }

With optimizations enabled, any C++ compiler should be able to compile
verify() away to a nop; so the inline verify() should be just as
efficient as the macro verify() - but still have the advantages of
proper name scoping and type safety that the macro verify() lacks.

Greg

Frederick Gotham

unread,
Oct 13, 2006, 5:43:37 PM10/13/06
to
BinglongX posted:

> This definition is different from MFC's, because the macro actually
> "returns" the evaluated expression, so that I can write code like this:
> bool ok = verify( start_bus() );


I prefer this:

bool ok = start_bus();

assert(ok);

, as it separates the "actual" code from the "debugging" code.

--

Frederick Gotham

Andrei Alexandrescu (See Website For Email)

unread,
Oct 13, 2006, 9:03:36 PM10/13/06
to
Frederick Gotham wrote:
> BinglongX posted:
>
>> This definition is different from MFC's, because the macro actually
>> "returns" the evaluated expression, so that I can write code like this:
>> bool ok = verify( start_bus() );
>
>
> I prefer this:
>
> bool ok = start_bus();
>
> assert(ok);
>
> , as it separates the "actual" code from the "debugging" code.

That introduces a gratuitous variable. if (!start_bus()) assert(false);
would avoid that.

Andrei

Lourens Veen

unread,
Oct 14, 2006, 4:51:25 PM10/14/06
to
Andrei Alexandrescu (See Website For Email) wrote:

> Frederick Gotham wrote:
>> BinglongX posted:
>>
>>> This definition is different from MFC's, because the macro
>>> actually "returns" the evaluated expression, so that I can write
>>> code like this:
>>> bool ok = verify( start_bus() );
>>
>>
>> I prefer this:
>>
>> bool ok = start_bus();
>>
>> assert(ok);
>>
>> , as it separates the "actual" code from the "debugging" code.
>
> That introduces a gratuitous variable. if (!start_bus())
> assert(false); would avoid that.

So how is

if (!start_bus()) assert(false);

better than

verify(start_bus());

then? Except that most people know what assert() does and you'll have
to explain verify() to them, but I think verify() looks nicer really.

Lourens

Alf P. Steinbach

unread,
Oct 14, 2006, 4:49:37 PM10/14/06
to
* Andrei Alexandrescu (See Website For Email):

> Frederick Gotham wrote:
>> BinglongX posted:
>>
>>> This definition is different from MFC's, because the macro actually
>>> "returns" the evaluated expression, so that I can write code like this:
>>> bool ok = verify( start_bus() );
>>
>> I prefer this:
>>
>> bool ok = start_bus();
>>
>> assert(ok);
>>
>> , as it separates the "actual" code from the "debugging" code.
>
> That introduces a gratuitous variable. if (!start_bus()) assert(false);
> would avoid that.

Ah, learned a new meaning of "gratuitous"... :-) Earlier in life I
maintained that if I didn't learn something new every day, I was getting
old. Now it's, if I don't learn something new every week... So thanks.

I have for some time come to appreciate the Perl style (is it Perl?) of
statements like

doThatThing || die // Some hypothetical language, Perl?

writing things like

doThatThing() || throwX( "Failed to do that thing!" ); // C++

which is very readable -- do that thing or else throw -- and can be
formatted to make the failure reporting code less visually imposing or
to make it stand out, depending on one's preferences and the context.

It would be nice to use essentially the same notation for both
exceptions and assertions; neither should ideally happen, it's just the
seriousness that differs, and in both cases following code shouldn't be
executed -- that's the essential guarantee.

Defining e.g.

define TERMINATE( e ) (assert(( e, false )), std::terminate(), true)

where the 'false' is to assert failure, the 'std::terminate()' is to
ensure termination if NDEBUG is defined (perhaps some feel a "hard"
exception would be more appropriate), and the 'true' is to support the
notation, one can write

doThatThing() || TERMINATE( "Failed to do that thing" );

e.g.

start_bus() || TERMINATE( "The bus failed to start, again." );

But I haven't tried this (just thought of it). Perhaps someone has, and
can offer further comments or insights, or bash the whole idea?

The only problem I see as I write this is that g++ complains about a
do-nothing expression on the left side of the comma operator. Which is
due to the argument to assert doing nothing. For g++ and Visual C++ in
Windows, which both use the MS runtime library, which reports assertion
failure as "Assertion failed: <text> <where>", one way to deal with that is

#define BECAUSE( e ) (void(e), false)
#define TERMINATE( e ) (assert(BECAUSE( e )), std::terminate(), true)

I don't know how that works out (if it does) with other compilers, i.e.
the resulting assertion text when the assertion fails.


Disclaimer: written late at night, or early in the morning. ;-)

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Frederick Gotham

unread,
Oct 14, 2006, 4:56:21 PM10/14/06
to
Andrei Alexandrescu (See Website For Email) posted:

>>> bool ok = verify( start_bus() );
>>
>>
>> I prefer this:
>>
>> bool ok = start_bus();
>>
>> assert(ok);
>>
>> , as it separates the "actual" code from the "debugging" code.
>
> That introduces a gratuitous variable. if (!start_bus()) assert(false);
> would avoid that.


The original code contained such a variable, so I took the liberty of
presuming it had a purpose. There's not much merit in writing "assert
(false)", as your error message will simply describe the error as "false"
rather than "start_bus()".

Essentially, I think the OP wants a function/macro that works exactly like
assert, except that it still evaluates its argument when in Release Mode.
Furthermore, I think the OP wants to retain the value of the expression. The
following might be a start:


#include <iostream>
#include <ostream>
#include <cstdlib>

template<class T>
T const &Verify(T const &expr,char const *const str)
{
if(!expr)
{
std::cerr << "VERIFY Failure. \"" __FILE__ "\": Line "
<< __LINE__ << " -- " << str << std::endl;

exit(EXIT_FAILURE);
}

return expr;
}

template<class T>
T &Verify(T &expr,char const *const str)
{
return const_cast<T&>(
Verify(const_cast<T const&>(expr),str)
);
}

#define VERIFY(expr) (Verify((expr),#expr))

class MyClass {
public:

MyClass() {}

operator double() const { return 56.8; }

operator bool() const { return false; }

};

int main()
{
MyClass obj;

double val = VERIFY(obj);

MyClass &r = VERIFY(obj);

std::cout << val << std::endl;
}

--

Frederick Gotham

Andrei Alexandrescu (See Website For Email)

unread,
Oct 14, 2006, 11:03:55 PM10/14/06
to
Lourens Veen wrote:
> So how is
>
> if (!start_bus()) assert(false);
>
> better than
>
> verify(start_bus());
>
> then? Except that most people know what assert() does and you'll have
> to explain verify() to them, but I think verify() looks nicer really.

You don't have to write verify() :o).

I actually do define VERIFY in some projects (only in uppercase though),
and I also think it's useful to have it return whatever it has verified.
(That's also what ENFORCE does.)


Andrei

Mark Van Peteghem

unread,
Oct 15, 2006, 11:21:09 AM10/15/06
to
BinglongX schreef:

Another pitfall is that the ability to use it as an expression makes
it not so extensible. When you want to add expressions, a level, a
group and or an optional action, you need to wrap it in a if-else
construct, so it can no longer be used as expression. That's what I
found out when I developed ModAssert. Actually I could make the
simplest assertions in ModAssert an expression, but I chose not to,
because if a developer later wants to add an expression, a level,
or ..., he would have to rewrite the way he uses the assertion
because the assertion is no longer an expression.

Furthermore I prefer all failing assertions (and verifys) to be
handled in the same way, by letting the code that is called by the
assertion do the error handling, so error handling in your code is
reduced, and changing the error handling code needs to be done in
only one place. To have some more flexibility than that, different
error handling could be done according to the level you assigned to
an assertion. I think you really don't need much more than that, so
why have all this (ugly) error handling in your code?

In ModAssert there is a clear distinction between unexpected errors
and expected errors. Unexpected errors are due to bugs, and ideally
an assertion that fails due to an unexpected error should terminate
the application. Expected errors are due to other factors (e.g. wrong
input by the user, network connection failed, ...), and requires
different error handling (return false, throw exception, ...).
ModAssert provides MOD_CHECK for these errors, that has a failure
action as its last argument. So your code would become

MOD_CHECK(check_function_args(), return false);

which is even shorter. The failure action is always executed, also in
non-debug mode.

See <http://modassert.sourceforge.net/> for more information.

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

Alf P. Steinbach

unread,
Oct 15, 2006, 12:38:55 PM10/15/06
to
* Mark Van Peteghem:
> BinglongX schreef:

>>
>> #include <assert.h>
>> #ifdef NDEBUG
>> #define verify(exp) (exp)
>> #else
>> #define verify(exp) ( (exp) || (assert(0),0) )
>> #endif
>>
>
[snip]

> Another pitfall is that the ability to use it as an expression makes
> it not so extensible. When you want to add expressions, a level, a
> group and or an optional action, you need to wrap it in a if-else
> construct, so it can no longer be used as expression.

Could you elaborate on this?

As far as I know this is a valid C++ statement:

42;

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

--

Mark Van Peteghem

unread,
Oct 15, 2006, 7:18:55 PM10/15/06
to
Alf P. Steinbach schreef:
> * Mark Van Peteghem:

>
>> Another pitfall is that the ability to use it as an expression makes
>> it not so extensible. When you want to add expressions, a level, a
>> group and or an optional action, you need to wrap it in a if-else
>> construct, so it can no longer be used as expression.
>>
>
> Could you elaborate on this?
>
> As far as I know this is a valid C++ statement:
>
> 42;
>

I didn't talk about statements, only expressions. Maybe I didn't
explain too well what I mean with adding expressions: I meant
expressions that are logged and displayed along with the other
information about a failed assertion; in ModAssert you can do that
with

MOD_ASSERT_P(a << b, a+b==10);

where a and b would be logged and displayed if the condition failed.
To be able to do that, MOD_ASSERT_P has to be defined as a if-else
construct, so it can no longer be used as an expression.

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Oct 16, 2006, 8:36:24 AM10/16/06
to
* Mark Van Peteghem:

> Alf P. Steinbach schreef:
>> * Mark Van Peteghem:
>>
>>> Another pitfall is that the ability to use it as an expression makes
>>> it not so extensible. When you want to add expressions, a level, a
>>> group and or an optional action, you need to wrap it in a if-else
>>> construct, so it can no longer be used as expression.
>>>
>> Could you elaborate on this?
>>
>> As far as I know this is a valid C++ statement:
>>
>> 42;
>
> I didn't talk about statements, only expressions. Maybe I didn't
> explain too well what I mean with adding expressions: I meant
> expressions that are logged and displayed along with the other
> information about a failed assertion; in ModAssert you can do that
> with
>
> MOD_ASSERT_P(a << b, a+b==10);
>
> where a and b would be logged and displayed if the condition failed.
> To be able to do that, MOD_ASSERT_P has to be defined as a if-else
> construct, so it can no longer be used as an expression.

I don't understand (but then, what else is new?).

Let's say I define MOD_ASSERT_P like this -- off the cuff:

#define MOD_ASSERT_P( sideEffect, assumption ) \
((assumption)? true : (sideEffect, std::terminate(), true))

perhaps conditionally defined with a do-nothing variant for NDEBUG.

I don't know the spec for MOD_ASSERT_P, but just assume the above
definition, unless it's way too different from what the real
MOD_ASSERT_P does.

The only potential problem I can think of at the moment is that the
value of 'assumption' is used more than once but shouldn't be evaluated
more than once, for fear of side-effects. And that one of those
potential evaluations is as an argument to 'assert'. But for that there
is both the solution of global variable + stringizing, and the solution
of requiring 'assumption' to have no side effects, which I think best.

A stringizing solution to that potential problem (not using a global
variable because here there's just one evaluation in addition to assert)
-- not that I would necessarily choose this solution -- might look
like this:

inline bool trueSansWarning() { return true; }

#define BECAUSE_NOT_TRUE( e ) (void(#e), false)

#define MOD_ASSERT_P( sideEffect, assumption ) \
((assumption)? trueSansWarning() : ( \
sideEffect, \
assert( BECAUSE_NOT_TRUE(assumption) ), std::terminate(), \
trueSansWarning() \
) \
)

Admittedly, I did have to put this last example through the compiler(s),
and admittedly, for MOD_ASSERT_P the ability to serve as an expression
seems to me to be of dubious value (as opposed to e.g. a TERMINATE macro
without any assumption to be tested), but what's the problem?

Well, except for Visual C++ warning about unreachable code (one of the
countless silly-warnings that should simply be turned off), I mean?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

--

Dave Harris

unread,
Oct 16, 2006, 9:04:54 AM10/16/06
to
al...@start.no (Alf P. Steinbach) wrote (abridged):

> > Another pitfall is that the ability to use it as an expression makes
> > it not so extensible. When you want to add expressions, a level, a
> > group and or an optional action, you need to wrap it in a if-else
> > construct, so it can no longer be used as expression.
>
> Could you elaborate on this?

I'm not the previous poster, but I imagine the issue is that we don't want
to evaluate some of the arguments if the condition succeeds. We could use
a conditional expression, like:

#define VERIFY( condition, report ) \
verify( condition, condition ? report : 0 )

but this evaluates the condition twice, which is unacceptable if it has
side effects. Code that evaluates the condition once and stores it for
re-use, like:

(typeof(condition) c(condition), verify( c, c ? report : 0 ) )

doesn't work because we can't declare variables in expressions. C++
expressions just aren't flexible enough to do all we'd wish.

Personally I agree with the original poster about the usefulness of VERIFY
being an expression, and I've never needed the complex assert reporting
that ModAssert offers, but Mark Van Peteghem's warning is well-made.

-- Dave Harris, Nottingham, UK.

Mark Van Peteghem

unread,
Oct 16, 2006, 12:34:26 PM10/16/06
to
Alf P. Steinbach schreef:

I've taken a look at how I implemented them, and examined why I
couldn't make them expressions (I already examined it once and found
out I couldn't, but later forgot why until now).

To be able to use the << operator for the expressions that I want to
be evaluated and shown, I use an object on the stack like this:

ModAssert::ParameterList parList; \
parList.SetExpressions(#params); \
parList << params; \

I could replace it with

&((*new ModAssert::ParameterList(#params)) << params)

and have the function that takes that object, delete it. Creating
objects on the heap is slower, but anyway. Or I could make it a
global object, but then I would have to make it threadsafe, not
optimal but possible. So the problem is not in adding expressions.

A real showstopper is the line

static bool modassert_display = true;\

which can be set to false if the assertion is repeatedly failing and
starts to get on your nerves, so the assertion is no longer shown.
This means I can't even use my simplest assertions as expressions.

Another showstopper is the optional action. The way I do it now it is
possible to have more than one statement by separating them with a
semicolon. You can even have braces (although it looks strange in a
macro). I don't think that is possible inside a ? : expression.

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

--

Alf P. Steinbach

unread,
Oct 16, 2006, 1:31:28 PM10/16/06
to
* Dave Harris:

> al...@start.no (Alf P. Steinbach) wrote (abridged):
>>> Another pitfall is that the ability to use it as an expression makes
>>> it not so extensible. When you want to add expressions, a level, a
>>> group and or an optional action, you need to wrap it in a if-else
>>> construct, so it can no longer be used as expression.
>> Could you elaborate on this?
>
> I'm not the previous poster, but I imagine the issue is that we don't want

> to evaluate some of the arguments if the condition succeeds. We could use
> a conditional expression, like:
>
> #define VERIFY( condition, report ) \
> verify( condition, condition ? report : 0 )
>
> but this evaluates the condition twice, which is unacceptable if it has
> side effects.

Well, as I mentioned (and exemplified) elsethread, that's easy to fix
technically, but as I also mentioned there, the better solution IMO is
to require the condition to not have side effects.


> Code that evaluates the condition once and stores it for
> re-use, like:
>
> (typeof(condition) c(condition), verify( c, c ? report : 0 ) )
>
> doesn't work because we can't declare variables in expressions.

Repeat above comment, + that this is an example of invalid code, but
what's the point of that? I mean, here's another example of invalid
code: 'void main() {}'.


> C++
> expressions just aren't flexible enough to do all we'd wish.

Agreed, I'd really like something like Smalltalk blocks. But that isn't
a problem here, AFAICS.


> Personally I agree with the original poster about the usefulness of VERIFY

> being an expression, and I've never needed the complex assert reporting
> that ModAssert offers, but Mark Van Peteghem's warning is well-made.

Sorry, I don't think so, presently. ;-)

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

--

John Moeller

unread,
Oct 16, 2006, 2:54:15 PM10/16/06
to
Frederick Gotham wrote:
> ...

> Essentially, I think the OP wants a function/macro that works exactly like
> assert, except that it still evaluates its argument when in Release Mode.
> Furthermore, I think the OP wants to retain the value of the expression.
The
> following might be a start:
>
>
> #include <iostream>
> #include <ostream>
> #include <cstdlib>
>
> template<class T>
> T const &Verify(T const &expr,char const *const str)
> {
> if(!expr)
> {
> std::cerr << "VERIFY Failure. \"" __FILE__ "\": Line "
> << __LINE__ << " -- " << str << std::endl;
>
> exit(EXIT_FAILURE);
> }
>
> return expr;
> }
>
> template<class T>
> T &Verify(T &expr,char const *const str)
> {
> return const_cast<T&>(
> Verify(const_cast<T const&>(expr),str)
> );
> }
>
> #define VERIFY(expr) (Verify((expr),#expr))
> ...

The template's a nice touch, except that you'd want to *pass* __FILE__
and __LINE__ to the function, or else they'll just point to where
Verify() is defined; it's better to include them in the macro so that
they point to where the macro is used:

template<class T>
T const &Verify(T const &expr,char const *const str,char const *const
file, int const line)
{
if(!expr)
{
std::cerr << "VERIFY Failure. \"" << file << "\": Line "
<< line << " -- " << str << std::endl;

exit(EXIT_FAILURE);
}

return expr;
}

template<class T>
T &Verify(T &expr,char const *const str,char const *const file, int
const line)
{
return const_cast<T&>(
Verify(const_cast<T const&>(expr),str,file,line)
);
}

#define VERIFY(expr) (Verify((expr),#expr,__FILE__,__LINE__))

Andrei Alexandrescu (See Website For Email)

unread,
Oct 16, 2006, 4:52:57 PM10/16/06
to
John Moeller wrote:
> #define VERIFY(expr) (Verify((expr),#expr,__FILE__,__LINE__))

Better yet, you can pack __FILE__ and __LINE__ info in one string, thus
saving passing around one parameter:

#define LOCUSINFO(file, line) file ":" TOSTR_INDIRECT(line)
#define TOSTR_INDIRECT(a) #a
#define VERIFY(expr) \
(Verify((expr),#expr, LOCUSINFO(__FILE__, __LINE__)))

or even collapse expr's string with the locus string:

#define VERIFY(expr) \
(Verify((expr),#expr " at " LOCUSINFO(__FILE__, __LINE__)))


Andrei

Alf P. Steinbach

unread,
Oct 16, 2006, 4:50:49 PM10/16/06
to
* Mark Van Peteghem:
>
> To be able to use the << operator for the expressions [in an
> assertion macro] that I want to

> be evaluated and shown, I use an object on the stack like this:
>
> ModAssert::ParameterList parList; \
> parList.SetExpressions(#params); \
> parList << params; \
>
> I could replace it with
>
> &((*new ModAssert::ParameterList(#params)) << params)
>
> and have the function that takes that object, delete it. Creating
> objects on the heap is slower, but anyway. Or I could make it a
> global object, but then I would have to make it threadsafe, not
> optimal but possible.

Note: threading is currently outside the language. It's necessary to go
outside the current language to have threading. Thus, solutions to
threading issues must in general be sought outside the current language.

If the above code is all that this variable is used for, however, why
don't you do

ModAssert::ParameterList(#params) << params

which is a nice little expression, with no extra costly dynamic allocation?

;-)


> So the problem is not in adding expressions.
>
> A real showstopper is the line
>
> static bool modassert_display = true;\
>
> which can be set to false if the assertion is repeatedly failing and
> starts to get on your nerves, so the assertion is no longer shown.
> This means I can't even use my simplest assertions as expressions.

That looks like a global to me.

Very easy to fix.

Just move the definition elsewhere, e.g. to an implementation file or
inside a function that you call.


> Another showstopper is the optional action. The way I do it now it is
> possible to have more than one statement by separating them with a
> semicolon. You can even have braces (although it looks strange in a
> macro). I don't think that is possible inside a ? : expression.

Right, there are restrictions. E.g. a loop is not an expression, but a
function call is. So a direct C++ loop construct can't be supplied by
the client code (although something like foreach can (where the Boost
library has some possibly useful "functional language" functionality)).

Anyway, it seems the problem has been reduced to or identified as: an
expression (e.g. an assert macro that should work as expression) must be
an expression, consisting in turn of expressions, and so on, which
prevents the client code from supplying /directly/ the most imperative
constructs, like loops, as side-effect macro arguments; in essence,
paraphrasing you, "you can't have braces in an expression", it must
stand on its own unsupported by braces.

I think that's a desirable feature, not an undesirable restriction.

So, IMHO that's good, not ungood.

But again, we're not discussing MOD_ASSERT_P (where the ability to serve
as an expression isn't really useful, as far as I can see), we're
discussing the general implications -- an earlier unspecified problem
-- of being able to use a "doer" macro as an expression. And as it
turned out it seems the unspecified problem was really a feature, also
for MOD_ASSERT_P, namely, that even if MOD_ASSERT_P wouldn't benefit
directly from being able to be used as an expression, it would benefit
indirectly, by offering a more reasonable interface to the client code.

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

--

Gennaro Prota

unread,
Oct 16, 2006, 9:11:15 PM10/16/06
to
On 16 Oct 2006 16:52:57 -0400, "Andrei Alexandrescu (See Website For
Email)" <SeeWebsit...@erdani.org> wrote:

>John Moeller wrote:
>> #define VERIFY(expr) (Verify((expr),#expr,__FILE__,__LINE__))
>
>Better yet, you can pack __FILE__ and __LINE__ info in one string, thus
>saving passing around one parameter:
>
>#define LOCUSINFO(file, line) file ":" TOSTR_INDIRECT(line)
>#define TOSTR_INDIRECT(a) #a
>#define VERIFY(expr) \
> (Verify((expr),#expr, LOCUSINFO(__FILE__, __LINE__)))
>
>or even collapse expr's string with the locus string:
>
>#define VERIFY(expr) \
> (Verify((expr),#expr " at " LOCUSINFO(__FILE__, __LINE__)))
>

I'm surprised that you recommend this. You loose your chances to treat
filename and line number separately without coupling the packing logic
with a corresponding unpacking logic at the receiving site (parsing);
what is the benefit?

--
Gennaro Prota

Binglong X

unread,
Oct 17, 2006, 9:41:52 AM10/17/06
to
John Moeller wrote:
>
> The template's a nice touch, except that you'd want to *pass* __FILE__
> and __LINE__ to the function, or else they'll just point to where
> Verify() is defined; it's better to include them in the macro so that
> they point to where the macro is used:
>
> template<class T>
> T const &Verify(T const &expr,char const *const str,char const *const
> file, int const line)
> {
> if(!expr)
> {
> std::cerr << "VERIFY Failure. \"" << file << "\": Line "
> << line << " -- " << str << std::endl;
>
> exit(EXIT_FAILURE);
> }
>
> return expr;
> }
>
> template<class T>
> T &Verify(T &expr,char const *const str,char const *const file, int
> const line)
> {
> return const_cast<T&>(
> Verify(const_cast<T const&>(expr),str,file,line)
> );
> }
>
> #define VERIFY(expr) (Verify((expr),#expr,__FILE__,__LINE__))

Thanks to everybody for pushing this topic so deep...

I am sorry that I did not explain the points clearly in the original
post, because I assumed people understand that I start from the well
known assert(exp).

I chose to use macro for three reasons, as some people already pointed
out:

1. With macro, especially using the readily available assert(), I do
not need to worry about passing the context dependent macros __FILE__
and __LINE__, because macro expansion is always inline. Let assert()
worry about it.

2. Using assert() instead of my own assertion handler, I basically
trust that the assertion handler in the library coming with the
compiler could do a better job than my code. Because assert() is fired
anyway in debugging, and often the development environment has better
control there, for example, allowing me to break the running, or ignore
the assertion, or whatever the stock assert() provides. Some of the
handling code may be platform dependent, and I do not want to get into
another round of #ifdef's for platform dependent processing. For
example, when you debug in a GUI, for example, MS Visual C++, you would
agree that you want VC to handle assertions. Say it in another way, I
want verify(exp) to have similar behavior with (platform dependent)
assert(exp) when exp evaluates to false.

3. Efficiency. Macro expansion is done before compiling, so you do not
rely on compiler optimization, you have control what gets compiled in
Debug or Release mode. Also note that the compilation is anyway
controlled by the macro NDEBUG, so why don't use it directly? The same
arguement also applies to assert(exp).

So, if assert(exp) is proved useful and widely used, verify(exp) is
just trying to leverage that with very simple definition, and the same
philosophy about macro vs. inline function that applies to assert(exp)
probably also applies to verify(exp). If you do not like assert(exp)
and do not use it, I guess verify(exp) is not useful to you.

Dave Harris

unread,
Oct 17, 2006, 3:36:51 PM10/17/06
to
al...@start.no (Alf P. Steinbach) wrote (abridged):
>> but this evaluates the condition twice, which is unacceptable if it
>> has side effects.
>
> Well, as I mentioned (and exemplified) elsethread, that's easy to fix
> technically,

If I understand what you are referring to, you fixed it by throwing away
the type and value of the expression, and simply returned true if it
succeeded. Which is reasonable because it was what the original poster's
code did in debug builds. I, on the other hand, was influenced by
Frederick Gotham's code, which used a template to preserve the type and
value. I think it would be OK to return 0 or false on failure, but if it
preserved the value on success it could be used like:

char *s = VERIFY( strdup( "test" ), "out of memory" );

which would be more useful.

(You mention also using a global variable to store the condition's value,
but I think that suffers from not being re-entrant or thread-safe. If you
had something else in mind, I missed it.)


> but as I also mentioned there, the better solution IMO is
> to require the condition to not have side effects.

The original poster wanted a replacement (or upgrade) for the MFC VERIFY.
The whole point of the MFC version is that it allows side effects in the
condition - otherwise you would just use ASSERT. (The MFC version does not
return the result, so if the expression has no side effects and no result
there's no need to include it in release builds.) So I see allowing side
effects in the condition as crucial part of the specification.

However, personally I think it would be OK to forbid side effects in the
report argument.

#define VERIFY( condition, report ) \

verify( (condition), (VerifyArgs() << report),\
__FILE__, __LINE__ )

where VerifyArgs is a class with template members which capture the types
and values of report, and verify itself is a template function which
returns the type and value of condition. I would expect the report
argument to be just a list of strings values and values:

int c = VERIFY( a+b, "a=" << a << " b=" << b );

so it shouldn't need side effects - provided, of course, the values aren't
displayed or logged or whatever if the check succeeds. I expect the author
of ModAssert would disagree with me.


> Repeat above comment, + that this is an example of invalid code, but
> what's the point of that?

I posted the invalid code to illustrate the problem: to show the kind of
thing we need to do, but can't.

-- Dave Harris, Nottingham, UK.

--

Mark Van Peteghem

unread,
Oct 18, 2006, 9:48:20 AM10/18/06
to
Alf P. Steinbach schreef:
> * Mark Van Peteghem:
>
>> ModAssert::ParameterList parList; \
>> parList.SetExpressions(#params); \
>> parList << params; \
>>
>> I could replace it with
>>
>> &((*new ModAssert::ParameterList(#params)) << params)
>>
>> and have the function that takes that object, delete it.
> If the above code is all that this variable is used for, however, why
> don't you do
>
> ModAssert::ParameterList(#params) << params
>
> which is a nice little expression, with no extra costly dynamic allocation?
>
Correct.

>> A real showstopper is the line
>>
>> static bool modassert_display = true;\
>>
>> which can be set to false if the assertion is repeatedly failing and
>> starts to get on your nerves, so the assertion is no longer shown.
>> This means I can't even use my simplest assertions as expressions.
>>
>
> That looks like a global to me.
>
> Very easy to fix.
>
> Just move the definition elsewhere, e.g. to an implementation file or
> inside a function that you call.
>

No, it should be defined in the macro, between braces (e.g. in an
if-else block), so every assertion has its own boolean that tells
whether *that* assertion should be displayed. A global boolean would
stop displaying *all* assertions.

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Oct 18, 2006, 9:43:40 AM10/18/06
to
* Dave Harris:

> al...@start.no (Alf P. Steinbach) wrote (abridged):
>>> but this evaluates the condition twice, which is unacceptable if it
>>> has side effects.
>> Well, as I mentioned (and exemplified) elsethread, that's easy to fix
>> technically,
>
> If I understand what you are referring to, you fixed it by throwing away
> the type and value of the expression, and simply returned true if it
> succeeded. Which is reasonable because it was what the original poster's
> code did in debug builds. I, on the other hand, was influenced by
> Frederick Gotham's code, which used a template to preserve the type and
> value. I think it would be OK to return 0 or false on failure, but if it
> preserved the value on success it could be used like:
>
> char *s = VERIFY( strdup( "test" ), "out of memory" );
>
> which would be more useful.

Featurism! ;-)

Well, a broadening of scope, because now it's very unclear what the
requirements are.

Anyway, the above is very possible, but I'd prefer

char const* s = strdup( "test" );
!!s || TERMINATE( "out of memory" );


> (You mention also using a global variable to store the condition's value,
> but I think that suffers from not being re-entrant or thread-safe. If you
> had something else in mind, I missed it.)

Irrelevant! ;-)

Note that

std::cout << "uh, well" << std::endl;

is not thread safe.

In short, the argument seems to be, "because C++ does not currently
support threading, nothing can be implemented in C++ (it would not be
thread safe)", which smacks of fallacy, at least to my tongue's taste
buds. As I've mentioned elsethread, the part you looked at for the
above comments, solutions to threading issues must be sought outside the
language. And at least on the system I'm most familiar with, it's no
problem.


>> but as I also mentioned there, the better solution IMO is
>> to require the condition to not have side effects.
>
> The original poster wanted a replacement (or upgrade) for the MFC VERIFY.
> The whole point of the MFC version is that it allows side effects in the
> condition - otherwise you would just use ASSERT. (The MFC version does not
> return the result, so if the expression has no side effects and no result
> there's no need to include it in release builds.) So I see allowing side
> effects in the condition as crucial part of the specification.

Yeah, well, no problem! ;-)


> However, personally I think it would be OK to forbid side effects in the
> report argument.
>
> #define VERIFY( condition, report ) \
> verify( (condition), (VerifyArgs() << report),\
> __FILE__, __LINE__ )
>
> where VerifyArgs is a class with template members which capture the types
> and values of report, and verify itself is a template function which
> returns the type and value of condition. I would expect the report
> argument to be just a list of strings values and values:
>
> int c = VERIFY( a+b, "a=" << a << " b=" << b );
>
> so it shouldn't need side effects - provided, of course, the values aren't
> displayed or logged or whatever if the check succeeds. I expect the author
> of ModAssert would disagree with me.
>
>
>> Repeat above comment, + that this is an example of invalid code, but
>> what's the point of that?
>
> I posted the invalid code to illustrate the problem: to show the kind of
> thing we need to do, but can't.

Need to? Can't? Please elaborate -- a specific, clear example would
be nice (so that we don't get into "It's impossible to drive faster than
80 kph!" "Yeah? I just did." "No, you used the forbidden turbo device."
"Yeah? Here's footage from 100 kph sans turbo device." "No, it must be
100 kph!" "Yeah? Here's that." "With only three wheels!" "Yeah? ...")

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu (See Website For Email)

unread,
Oct 18, 2006, 9:44:25 AM10/18/06
to
Gennaro Prota wrote:
> On 16 Oct 2006 16:52:57 -0400, "Andrei Alexandrescu (See Website For
> Email)" <SeeWebsit...@erdani.org> wrote:
>
>> John Moeller wrote:
>>> #define VERIFY(expr) (Verify((expr),#expr,__FILE__,__LINE__))
>> Better yet, you can pack __FILE__ and __LINE__ info in one string, thus
>> saving passing around one parameter:
>>
>> #define LOCUSINFO(file, line) file ":" TOSTR_INDIRECT(line)
>> #define TOSTR_INDIRECT(a) #a
>> #define VERIFY(expr) \
>> (Verify((expr),#expr, LOCUSINFO(__FILE__, __LINE__)))
>>
>> or even collapse expr's string with the locus string:
>>
>> #define VERIFY(expr) \
>> (Verify((expr),#expr " at " LOCUSINFO(__FILE__, __LINE__)))
>>
>
> I'm surprised that you recommend this. You loose your chances to treat
> filename and line number separately without coupling the packing logic
> with a corresponding unpacking logic at the receiving site (parsing);
> what is the benefit?

The benefit is obvious: in normal execution the cost is reduced
(probably significantly). When a verification fails, speed is not critical.

Andrei

Andrei Alexandrescu (See Website For Email)

unread,
Oct 18, 2006, 3:15:59 PM10/18/06
to
Alf P. Steinbach wrote:
> * Dave Harris:

>> char *s = VERIFY( strdup( "test" ), "out of memory" );
>>
>> which would be more useful.
>
> Featurism! ;-)
>
> Well, a broadening of scope, because now it's very unclear what the
> requirements are.
>
> Anyway, the above is very possible, but I'd prefer
>
> char const* s = strdup( "test" );
> !!s || TERMINATE( "out of memory" );

I think it's important to save vertical space, which is always at a
premium.

>> (You mention also using a global variable to store the condition's value,
>> but I think that suffers from not being re-entrant or thread-safe. If you
>> had something else in mind, I missed it.)
>
> Irrelevant! ;-)
>
> Note that
>
> std::cout << "uh, well" << std::endl;
>
> is not thread safe.
>
> In short, the argument seems to be, "because C++ does not currently
> support threading, nothing can be implemented in C++ (it would not be
> thread safe)", which smacks of fallacy, at least to my tongue's taste
> buds.

I think the problem is cut clearer in this case: global shared state
invites a host of problems, among which threading problems. It's not a
fallacy.


Andrei

--

Alf P. Steinbach

unread,
Oct 19, 2006, 9:21:11 AM10/19/06
to
* Mark Van Peteghem:
> ... every assertion [should have] its own boolean that tells

> whether *that* assertion should be displayed. A global boolean would
> stop displaying *all* assertions.

Just use the file name and line number to identify the assertion.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Dave Harris

unread,
Oct 19, 2006, 8:49:49 PM10/19/06
to
al...@start.no (Alf P. Steinbach) wrote (abridged):
> Featurism! ;-)

As I said, it's something I picked up from another post, not something I
added myself.


> Anyway, the above is very possible, but I'd prefer
>
> char const* s = strdup( "test" );
> !!s || TERMINATE( "out of memory" );

The benefit is more in situations where there is no named variable. For
example:

return VERIFY( strdup( "test" ), "out of memory" );

if (some_condition)
some_function( VERIFY( strdup( "test" ), "out of memory" ) );

In the second example, adding a variable would turn one line into three
lines plus braces - potentially 5 lines depending on layout. As Andrei
says, vertical space is precious.

There may be an element of psychology involved, too. In my experience,
some programmers are reluctant to use assertions at all, so we need to
make it as unintrusive and transparent as we can. That's part of why
preserving the expression's type and value matter.


> > (You mention also using a global variable to store the condition's
> > value, but I think that suffers from not being re-entrant or
> > thread-safe.

> [...]


>
> In short, the argument seems to be, "because C++ does not currently
> support threading, nothing can be implemented in C++ (it would not be
> thread safe)", which smacks of fallacy, at least to my tongue's taste
> buds.

Did you miss the word "re-entrant"? I had in mind code like:

return VERIFY( p->value(), p->name() << " has bad value" );

where p->name() may itself use VERIFY. Global variables are problematic
even without multi-threading.

In any case, even if you don't use threads, they are part of the modern
C++ landscape for many of us. The original poster wanted to know if there
were any pitfalls in the approach he was taking. In that context it is
certainly worth mentioning threading issues.


> Please elaborate -- a specific, clear example would be nice

How about:
return VERIFY( p->value(), "bad value from: " << p->dump() );

where:

(a) p->value() should be evaluated exactly once regardless of
whether checks are enabled.
(b) The type and value returned should be the same as the type and
value of p->value(). (You can return 0 on failure, but it'd be
even better if that wasn't necessary.)
(c) p->value() may also use VERIFY.
(d) p->dump() may also use VERIFY.
(e) The code should be thread-safe.
(f) p->dump() is evaluated only if the check fails.

That's listed in my personal order of importance. (f) is the one I would
drop. The ideal VERIFY would satisfy them all.

-- Dave Harris, Nottingham, UK.

--

Mark Van Peteghem

unread,
Oct 20, 2006, 5:46:32 AM10/20/06
to
Dave Harris schreef:

> How about:
> return VERIFY( p->value(), "bad value from: " << p->dump() );
>
> where:
>
> (a) p->value() should be evaluated exactly once regardless of
> whether checks are enabled.
> (b) The type and value returned should be the same as the type and
> value of p->value(). (You can return 0 on failure, but it'd be
> even better if that wasn't necessary.)
> (c) p->value() may also use VERIFY.
> (d) p->dump() may also use VERIFY.
> (e) The code should be thread-safe.
> (f) p->dump() is evaluated only if the check fails.
>
> That's listed in my personal order of importance. (f) is the one I would
> drop. The ideal VERIFY would satisfy them all.
>

If you want (a) and (b), I think it has to be done with a template
function (or I have too little imagination to use macros well). That
means you should drop (f). But I don't see why (c) and (d) could be a
problem. These methods return before the template function is
entered.

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Mark Van Peteghem

unread,
Oct 20, 2006, 5:45:59 AM10/20/06
to
Alf P. Steinbach schreef:

> * Mark Van Peteghem:
>
>> ... every assertion [should have] its own boolean that tells
>> whether *that* assertion should be displayed. A global boolean would
>> stop displaying *all* assertions.
>>
>
> Just use the file name and line number to identify the assertion.

That solution will probably work well, with just a small problem:
VERIFY may be nested, e.g.

return VERIFY(foo(VERIFY(bar())));

This is an excellent example of saving vertical space, but in such
cases the two VERIFY's will have the same file name and line number.
One of them may fail frequently, so the developer may stop displaying
it, but then he will not know when the other one fails.

I wish C++ had __COLUMN__ :-)

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Oct 20, 2006, 10:28:04 AM10/20/06
to
* Mark Van Peteghem:

> Alf P. Steinbach schreef:
>> * Mark Van Peteghem:
>>
>>> ... every assertion [should have] its own boolean that tells
>>> whether *that* assertion should be displayed. A global boolean would
>>> stop displaying *all* assertions.
>>>
>>
>> Just use the file name and line number to identify the assertion.
>
> That solution will probably work well, with just a small problem:
> VERIFY may be nested, e.g.
>
> return VERIFY(foo(VERIFY(bar())));
>
> This is an excellent example of saving vertical space, but in such
> cases the two VERIFY's will have the same file name and line number.
> One of them may fail frequently, so the developer may stop displaying
> it, but then he will not know when the other one fails.
>
> I wish C++ had __COLUMN__ :-)

As I understand it this is in support of debugging, because otherwise
the developer could just change VERIFY to e.g. DONTVERIFY and recompile.

Well, turn it around, like

#define VERIFYIF( enabled, expr ) \
(enabled? VERIFY( expr ) : expr )

return VERIFYIF( bah27_enabled, foo( VERIFYIF( snarkum45, bar() ) ) )

where bah28_enabled and snarkum45 are enabling flags.

This exhanges one cost (work expended to find the thing that disables a
given assertion) for another cost (more visual clutter in client code),
but I think if the code is sufficiently spaghetti that this device is
"needed", then the little visual clutter won't matter, and it gives much
greater control & flexibility.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Oct 20, 2006, 10:26:43 AM10/20/06
to
* Dave Harris:

> al...@start.no (Alf P. Steinbach) wrote (abridged):
>> Featurism! ;-)
> As Andrei says, vertical space is precious.

Bah, my monitor can be turned 90 degrees. Not that I use that feature.
To save vertical space & typing I just leave the strong arguments
against side-effect operators & terse code, and ditto strong arguments
for clarity and readability, to the reader's imagination.


[snip]


>> Please elaborate -- a specific, clear example would be nice
>
> How about:
> return VERIFY( p->value(), "bad value from: " << p->dump() );
>
> where:
>
> (a) p->value() should be evaluated exactly once regardless of
> whether checks are enabled.
> (b) The type and value returned should be the same as the type and
> value of p->value(). (You can return 0 on failure, but it'd be
> even better if that wasn't necessary.)
> (c) p->value() may also use VERIFY.
> (d) p->dump() may also use VERIFY.
> (e) The code should be thread-safe.
> (f) p->dump() is evaluated only if the check fails.
>
> That's listed in my personal order of importance. (f) is the one I would
> drop. The ideal VERIFY would satisfy them all.

Hopefully there will now not be a follow up requesting higher speed
and/or no turbo device and/or only three wheels or whatever. ;-)

Let's start with (f) then, that's the conditional operator, which is
nice enough to only evaluate the selected result sub-expression:

(valueExpr? true : (log << logExpression, std::terminate(), true))

Now the goal is to replace the first 'true' with the valueExpr's value,
and here it doesn't suffice to delegate the whole expression to a
function, because the function arguments would be evaluated, including
logExpression, which by (f) shouldn't be evaluated in all cases. But a
function can store and retrieve the valueExpr value. Then it looks like

(setTempValue<TYPEOF(valueExpr)>( valueExpr, ASSERTIONID )
? tempValue<TYPEOF(valueExpr)>( ASSERTIONID )
: (
log << logExpression,
std::terminate(),
dummyTempValue<TYPEOF(valueExpr)>( ASSERTIONID )
)
)

where ID is a unique id formed from __FILE__ and __LINE__, and the
functions used, setTempValue<>(), tempValue<>() and dummyTempValue<>(),
provide static thread local storage to satisfy requirement (e).

Thread locale storage is outside the scope of the current language, but
evidently there is a fairly portable implementation in the Boost
library, a class boost::thread_specific_ptr.

Since this is evaluated from left to right, top to bottom, (d) is also
satisified, and so is (c), (b) and (a). Another feature of that
evaluation sequence is that an ASSERTIONID to identify the relevant temp
variable is really superflous. There is pure stack behavior for the
simulated temporary variables, and so the expression can be both
simplified & optimized:

(setTempValue<TYPEOF(valueExpr)>( valueExpr )
? tempValue<TYPEOF(valueExpr)>( valueExpr )
: (
log << logExpression,
std::terminate(),
dummyTempValue<TYPEOF(valueExpr)>()
)
)

Of course that TYPEOF in there is a bit suspicious. For compilers that
don't support de facto standard 'typeof' nor the newfangled C++0x
some-unreadable-designator-of-the-same, true TYPEOF can be simulated by
registering the relevant types via a declarative macro, e.g.
REGISTER_TYPEOF_TYPE. That's my turbo device this time. ;-)


--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Oct 20, 2006, 10:30:09 AM10/20/06
to
* Mark Van Peteghem:

> Dave Harris schreef:
>> How about:
>> return VERIFY( p->value(), "bad value from: " << p->dump() );
>>
>> where:
>>
>> (a) p->value() should be evaluated exactly once regardless of
>> whether checks are enabled.
>> (b) The type and value returned should be the same as the type and
>> value of p->value(). (You can return 0 on failure, but it'd be
>> even better if that wasn't necessary.)
>> (c) p->value() may also use VERIFY.
>> (d) p->dump() may also use VERIFY.
>> (e) The code should be thread-safe.
>> (f) p->dump() is evaluated only if the check fails.
>>
>> That's listed in my personal order of importance. (f) is the one I
>> would drop. The ideal VERIFY would satisfy them all.
>>
>
> If you want (a) and (b), I think it has to be done with a template
> function

Yep.


[snip]


> That means you should drop (f).

Nope, although the implementation would probably be simpler & possibly
(I'm not sure) also more efficient without that requirement.


> But I don't see why (c) and (d) could be a
> problem. These methods return before the template function is
> entered.

I posted a reply to Dave Harris where I sketched a solution, but I hit
the "Send" button by mistake instead of saving a draft so it didn't
contain more than that sketch (and besides, it had lots of typos).

An actual C++ code version of that description is available at <url:
http://home.no.net/alfps/misc/verify.cpp>; compiles with MSVC and g++.

The code is incomplete in the sense that it doesn't support all
conceivable types, e.g., I haven't put in support for reference to
non-const expression. It's also incomplete in that I haven't added
threading support, which is somewhat system dependent; the places (two
of them) are marked with TODO comments. It's a proof-of-concept.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Mark Van Peteghem

unread,
Oct 20, 2006, 1:20:25 PM10/20/06
to
Mark Van Peteghem schreef:

> Alf P. Steinbach schreef:
>> * Mark Van Peteghem:
>>
>>> ... every assertion [should have] its own boolean that tells
>>> whether *that* assertion should be displayed. A global boolean would
>>> stop displaying *all* assertions.
>>>
>>
>> Just use the file name and line number to identify the assertion.
>
> That solution will probably work well, with just a small problem:
> VERIFY may be nested, e.g.
>
> return VERIFY(foo(VERIFY(bar())));
>
> This is an excellent example of saving vertical space, but in such
> cases the two VERIFY's will have the same file name and line number.
> One of them may fail frequently, so the developer may stop displaying
> it, but then he will not know when the other one fails.

I just realize I can use the text of the condition as well. If
they're nested, they can't possibly be the same. I think it's best
to use a set, ordered on line number first (most efficient), then on
condition and then on filename; I choose condition before filename,
because filenames are likely to start with a lot of identical
characters, so it takes longer to compare.

Now it's only a problem if they put two identical assertions on the
same line.

Mark Van Peteghem

unread,
Oct 21, 2006, 6:25:20 AM10/21/06
to
Alf P. Steinbach schreef:

> An actual C++ code version of that description is available at <url:
> http://home.no.net/alfps/misc/verify.cpp>; compiles with MSVC and g++.

A very inventive solution. But the DECLARE_TYPEOF_TYPE seems a bit
cumbersome. If you link independent libraries in an application, you
can't be sure that there will be no number that was chosen twice. I
also have no idea how performant thread specific storage is, but I
guess it's faster than converting the value to a string every time.

--
Mark dot Van dot Peteghem at q-mentum dot com
http://www.q-mentum.com -- easier and more powerful unit testing

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Oct 22, 2006, 3:58:41 AM10/22/06
to
* Mark Van Peteghem:

> Alf P. Steinbach schreef:
>> An actual C++ code version of that description is available at <url:
>> http://home.no.net/alfps/misc/verify.cpp>; compiles with MSVC and g++.
>
> A very inventive solution. But the DECLARE_TYPEOF_TYPE seems a bit
> cumbersome. If you link independent libraries in an application, you
> can't be sure that there will be no number that was chosen twice.

An anonymous namespace allows declarations per compilation unit, a
systematic numbering scheme allows more, and a C++0x-ready compiler (or
one supporting g++-like typeof) allows just about anything.

However, the VERIFY signature & effect isn't my design: it had to
conform to Dave Harris' example and requirements.

If I were to do this kind of VERIFY supporting side-effects, I'd just
let the client code pass in the required type -- explicit code, not
implicit code. ;-)

And actually, now that you mention it the type-detecting version should
ideally have been implemented in terms of one with the type as argument.

Gaining the best of both worlds.


> I
> also have no idea how performant thread specific storage is, but I
> guess it's faster than converting the value to a string every time.

Thread local storage is very fast. As I recall in Windows it uses one
indirection (off a processor register); after all, the processor must
know which thread it's executing. However, it can be limited in the
number of pointers that can be allocated. And it can be dangerous. As
an example, Visual C++ provides direct support (a language extension[1])
for thread local storage, but as I recall not compatible with explicitly
loaded DLLs (a DLL is the Windows variant of dynamic library).


Notes:
[1] "__declspec( thread )"

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Oct 22, 2006, 3:59:45 AM10/22/06
to
{ A little more context would have been much more helpful. -mod }

* Mark Van Peteghem:
[...]

Argh, please ignore my comment about anon namespace in the follow-up
posted a few minutes ago. I wasn't thinking. :-(

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Dave Harris

unread,
Nov 6, 2006, 2:34:56 PM11/6/06
to
al...@start.no (Alf P. Steinbach) wrote (abridged):
> (setTempValue<TYPEOF(valueExpr)>( valueExpr )
> ? tempValue<TYPEOF(valueExpr)>( valueExpr )
> : (
> log << logExpression,
> std::terminate(),
> dummyTempValue<TYPEOF(valueExpr)>()
> )
> )

That's clever. The idea of using a manual stack gets around the problems I
had in mind. Does it work in cases like:
return VERIFY( a, b ) + VERIFY( c, d );

I think the compiler is allowed to interleave evaluation of the two sides
of the plus, which could upset the stack operations.

Of course, I didn't mention this use-case, so you may consider it a change
to the specification.

-- Dave Harris, Nottingham, UK.

--

Alf P. Steinbach

unread,
Nov 6, 2006, 7:15:06 PM11/6/06
to
* Dave Harris:

> al...@start.no (Alf P. Steinbach) wrote (abridged):
>> (setTempValue<TYPEOF(valueExpr)>( valueExpr )
>> ? tempValue<TYPEOF(valueExpr)>( valueExpr )
>> : (
>> log << logExpression,
>> std::terminate(),
>> dummyTempValue<TYPEOF(valueExpr)>()
>> )
>> )
>
> That's clever. The idea of using a manual stack gets around the problems I
> had in mind. Does it work in cases like:
> return VERIFY( a, b ) + VERIFY( c, d );
>
> I think the compiler is allowed to interleave evaluation of the two sides
> of the plus, which could upset the stack operations.

The question is whether in the expression

(A0? A1 : A2 ) + (B0? B1 : B2)

it's possible that A0 and B0 are both evaluated before any of A1, A2, B1
or B2?

I didn't think of that, and I don't know the answer.

In favor of answer "yes" (dang!) is §5/4, which leaves the order of
evaluation of operands unspecified "Except where noted".

In favor of answer "no" (good) is the definition of ?:, where there is
effectively a sequence point after the ?. And a sequence point isn't
much of a point if two different places in the code (namely, the two ?'s
above) can map to the same point. Then it'd be only a point in time.

If the answer to the question turns out to be "yes", then one solution
might be to update the documentation (not a bug, it's a feature).


> Of course, I didn't mention this use-case, so you may consider it a change
> to the specification.

Yep. ;-)

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Martin Bonner

unread,
Nov 7, 2006, 3:18:23 PM11/7/06
to

Alf P. Steinbach wrote:
> The question is whether in the expression
>
> (A0? A1 : A2 ) + (B0? B1 : B2)
>
> it's possible that A0 and B0 are both evaluated before any of A1, A2, B1
> or B2?
>
> I didn't think of that, and I don't know the answer.
>
> In favor of answer "yes" (dang!) is §5/4, which leaves the order of
> evaluation of operands unspecified "Except where noted".

Absolutely. Sequence points add a PARTIAL ordering to sub-expressions.

> In favor of answer "no" (good) is the definition of ?:, where there is
> effectively a sequence point after the ?. And a sequence point isn't
> much of a point if two different places in the code (namely, the two ?'s
> above) can map to the same point. Then it'd be only a point in time.

The sequence points after the evaluation of A0 guarantees that A0 will
be completely evaluated before A1 or A2 is evaluated. Similarly for
B0/B1/B2. There is no guarantee about the relative evaluation of A0
and B0.


--

Frederick Gotham

unread,
Nov 7, 2006, 3:25:24 PM11/7/06
to
Alf P. Steinbach:

> The question is whether in the expression
>
> (A0? A1 : A2 ) + (B0? B1 : B2)
>
> it's possible that A0 and B0 are both evaluated before any of A1, A2, B1
> or B2?

My current understanding is as follows:

The order of evaluation of the operands of the built-in addition
operator is
unspecified. One restraint though is that the operands can't be evaluted
concurrently, i.e. one operand must complete its evaluation prior to the
second operand beginning its evaluation. So it seems one of the following
sub-expressions must be evaluated first:

Sub-expression 1: a0 ? a1 : a2
Sub-expression 2: b0 ? b1 : b2

Let's say that the implementation chose to evaluate Sub-expression 1 first.
Well, by my understanding, it would be impossible for b0 to be evaluated
before a1 or a2 because the operands of the addition operator cannot be
evaluated concurrently.

--

Frederick Gotham

Martin Bonner

unread,
Nov 7, 2006, 6:58:36 PM11/7/06
to

Frederick Gotham wrote:
> Alf P. Steinbach:
>
> > The question is whether in the expression
> >
> > (A0? A1 : A2 ) + (B0? B1 : B2)
> >
> > it's possible that A0 and B0 are both evaluated before any of A1, A2, B1
> > or B2?
>
> My current understanding is as follows:
>
> The order of evaluation of the operands of the built-in addition
> operator is unspecified.
Correct.

> One restraint though is that the operands can't be evaluted
> concurrently,
I don't see that in the standard. Are you thinking of the
clarification to the C standard some years ago that FUNCTIONS cannot be
evaluated in parallel?


--

Arkadiy

unread,
Nov 7, 2006, 6:54:50 PM11/7/06
to
Alf P. Steinbach wrote:

> Of course that TYPEOF in there is a bit suspicious. For compilers that
> don't support de facto standard 'typeof' nor the newfangled C++0x
> some-unreadable-designator-of-the-same, true TYPEOF can be simulated by
> registering the relevant types via a declarative macro, e.g.
> REGISTER_TYPEOF_TYPE. That's my turbo device this time. ;-)

You may also want to look at the Boost.Typeof library, that is going to
be a part of upcomming Boost 1.34.

Regards,
Arkadiy


--

James Kanze

unread,
Nov 8, 2006, 7:11:47 AM11/8/06
to
Frederick Gotham wrote:
> Alf P. Steinbach:

> > The question is whether in the expression

> > (A0? A1 : A2 ) + (B0? B1 : B2)

> > it's possible that A0 and B0 are both evaluated before any
> > of A1, A2, B1 or B2?

> My current understanding is as follows:

> The order of evaluation of the operands of the built-in
> addition operator is unspecified. One restraint though is
> that the operands can't be evaluted concurrently, i.e. one
> operand must complete its evaluation prior to the second
> operand beginning its evaluation.

What makes you say that? A compiler most definitly is allowed
to evaluate the two operands in parallel, and it would be a very
poor compiler that didn't, at least when optimization is active.
The only restriction concerning parallelisation (other than
those due to sequence points) is that code in two functions may
not execute in parallel---once the sequence point of calling a
function has been executed, no further code in the expression
(including that of calling another function) may be executed
until the sequence point of returning from that function has
been executed. (Modulo "as if", of course.)

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


--

0 new messages