I would like to keep assertions but get rid of macros in my code.
Are there examples of the assert function built on templates?
Thanks,
Daniel
"DeMarcus" <use_my_a...@hotmail.com> wrote in message
news:4b9e2335$0$286$1472...@news.sunsite.dk...
Not using assert because it is a macro is irrational. Just because we have
templates does not mean we should no longer use macros: macros are a useful
language (preprocessor) feature.
You can write your own assert-like function though and it doesn't need to be
a template, all it needs to do is call std::abort if the assertion is false.
/Leigh
"Leigh Johnston" <le...@i42.co.uk> wrote in message
news:YL-dnXQDZvHtsQPW...@giganews.com...
if you want to do something other than call std::abort (for example throw an
exception) you could do:
template<class X, class A> inline void Assert(A assertion)
{
if (!assertion) throw X();
}
class Bad_arg {};
void f(int* p)
{
Assert<Bad_arg>(p != 0);
}
-- from Stroustrup (TC++PL)
/Leigh
If you want the exact replacement of assert(), you cannot: with NDEBUG
assert() will expand to nothing which mean that parameters will no be
evaluated.
Without macro, you cannot simply remove code. You can do somewhat
similar with a shortcut operator:
Assertion::assert( !Assertion::activated || /* assertion */ );
With
struct Assertion
{
static const bool activated = false;
//....
};
This is hideous.
Concerning getting __FILE__ and __LINE__, a template cannot get it
from the point it is called (because they are replaced by the
preprocessor).
--
Michael
Maybe it's irrational to avoid macros but I want to pass log messages in
the assertion, hence I need to write my own assert() anyway and thought
I could do it with a little inspiration from some other template assert.
The Stroustrup tip looks like something I could get ideas from.
Thanks!
Daniel
Actually the reason why I want to write my own assert is to be able to
pass messages to a system log. I will actually write something similar
to the code you wrote. You are also completely right about the file and
line information, but I pass that on in the log massage today, so then
everything should be set.
As far as I understand, the only two reasons for assert() being a macro are:
* To provide __FILE__ and __LINE__ automatically.
* To save 1 nanosecond when disabling the assert(), having no code at
all compared to an if( assertionEnabled && !condition ).
Or are there more reasons?
Thanks,
Daniel
to expand the condition as part of the assertion message?
assert (x > 0);
=>
"assertion failed x > 0 pippo.c line: 42
how does a template get the "x > 0" into the string?
Rather hacky code:-
#define ASSERT_X(A) ((A) ? (void)0 \
: logger.throw_assert (__FILE__, __LINE__,
#A))
void Log::throw_assert (const char *file, int line, const char *msg)
{
fatal (file, line, "Assertion failed: %s", msg);
throw runtime_error ("Assertion failed");
}
in this code logger was a global error reporter (it was (almost) the
only global, honest!). fatal() wrote a suitable mmessage to the
logger. Looking again I'd have thrown std::logic_error
Why no _NDEBUG? Because I don't turn my asserts off! See another
thread :-)
--
Nick Keighley
I don't want to be rude, but that information is not useful anyway. You
have a failed assertion and go to line 42 in pippo.c. There you will see
(x > 0) anyway. What may be useful though, is if you could show the
value of x in the output, but that's not harder in a template than in an
assert().
For me, that don't like macros, I think that the only thing speaking for
the macro assert is the automatic insertion of __FILE__ and __LINE__.
> Rather hacky code:-
>
> #define ASSERT_X(A) ((A) ? (void)0 \
> : logger.throw_assert (__FILE__, __LINE__,
> #A))
>
>
> void Log::throw_assert (const char *file, int line, const char *msg)
> {
> fatal (file, line, "Assertion failed: %s", msg);
> throw runtime_error ("Assertion failed");
> }
>
>
> in this code logger was a global error reporter (it was (almost) the
> only global, honest!). fatal() wrote a suitable mmessage to the
> logger. Looking again I'd have thrown std::logic_error
>
> Why no _NDEBUG? Because I don't turn my asserts off! See another
> thread :-)
>
I agree with you. I prefer to keep the asserts on all the time rather
than saving a couple of nanoseconds here and there.
I imagine you could do it with lambdas
template<typename T>
void assert(T t) { if(!t()) std::abort(); }
...
assert( [&]{ return foo && bar; });
<snip>
> Concerning getting __FILE__ and __LINE__, a template cannot get it
> from the point it is called (because they are replaced by the
> preprocessor).
>
I am using this:
http://www.ibm.com/developerworks/linux/library/l-cppexcep.html
but it is only for linux. Not sure if something similar can be done on
other systems.
Assertions are one of the very few justifications for keep macros in C++
code. You simply can't duplicate their functionality by any other means.
--
Ian Collins
At the risk of stating the obvious: a reason for a compiler to
implement assert as a macro is because the C++ standard states that it
is to be a macro. Which in turn is influenced by the C standard.
A reason why the C standard might have made it a macro is to allow its
behavior to be defined at the point where it is most recently
#included. For a traditional function call, the behavior is defined
when it is first #included. Here is an example:
#define NDEBUG
#include <cassert>
void func1()
{
assert(0);
}
#undef NDEBUG
#include <cassert>
void func2()
{
assert(0);
}
int main()
{
func1();
func2();
return 0;
}
Here, func1 does nothing because NDEBUG was defined when cassert was
#included. func2 will abort because NDEBUG was not defined when
cassert was #included the second time. If you tried to do this with
functions you might have more difficulty. For instance, trying to
#include <cassert> twice would cause build complaints because assert
is defined twice, or (if include guards are used in cassert) the
behavior would be fixed upon the first inclusion of cassert, and the
second inclusion would be a no-op.
We might have some clever ways to get around this with C++ features
(templates or lambdas) but they aren't available for C.
That's beautiful. Thanks for the tip!
> I would like to keep assertions but get rid of macros in my code.
> Are there examples of the assert function built on templates?
The whole point of assert is that you can turn it off on a
function by function basis. This means that the implementation
must do #undef, before redefining it. And there's no way to
undef anything but a macro.
Also, good implementations of assert generally include a use of
__FILE__ and __LINE__. Which also requires a macro,
--
James Kanze
> > If you want the exact replacement of assert(), you cannot:
> > with NDEBUG assert() will expand to nothing which mean that
> > parameters will no be evaluated.
> > Without macro, you cannot simply remove code. You can do
> > somewhat similar with a shortcut operator:
> > Assertion::assert( !Assertion::activated || /* assertion */ );
> > With
> > struct Assertion
> > {
> > static const bool activated = false;
> > //....
> > };
> > This is hideous.
> > Concerning getting __FILE__ and __LINE__, a template cannot
> > get it from the point it is called (because they are
> > replaced by the preprocessor).
> Actually the reason why I want to write my own assert is to be
> able to pass messages to a system log.
In which case, you're not talking about assert, but something
else. (Which will probably involve macros as well, in order to
automatically insert __FILE__ and __LINE__.)
[...]
> As far as I understand, the only two reasons for assert() being a macro are:
> * To provide __FILE__ and __LINE__ automatically.
> * To save 1 nanosecond when disabling the assert(), having no code at
> all compared to an if( assertionEnabled && !condition ).
> Or are there more reasons?
The main one is that you can turn it off on a function by
function basis, rather than only globally.
--
James Kanze
Hm, that's an interesting solution. I'll think about it.
Thanks,
Daniel
There are some reasons why seeing the expression is useful though:
(1) It makes it easier to locate the exact assertion if the code-base
has changed since the executable was made (especially useful when
there's a bunch of adjacent assertions).
(2) During development, the assertion expression is often immediately
recognizable to the programmer ("oh, it's the damn XXX problem again"),
and so can speed up debugging. [For commonly occurring problems, they
may recognize a filename / line number too, but it's probably easier
with an expression, especially if variables names etc are informative.]
-Miles
--
Faith, n. Belief without evidence in what is told by one who speaks without
knowledge, of things without parallel.
Ok, I agree. You have a point there.
Having if( assertionEnabled && !condition ) is exactly the same as
assert() if assertionEnabled is a compile time constant (the compiler
will simply remove the code). But it is a bit tedious to repeat.
> >> Or are there more reasons?
>
> > to expand the condition as part of the assertion message?
>
> > assert (x > 0);
> > =>
> > "assertion failed x > 0 pippo.c line: 42
>
> > how does a template get the "x > 0" into the string?
If you have the file and the line passed to the template, you also get
the content of the condition (you pass #condition in parameter ).
[snip]
--
Michael
> The whole point of assert is that you can turn it off on a
> function by function basis. This means that the implementation
> must do #undef, before redefining it. And there's no way to
> undef anything but a macro.
>
> Also, good implementations of assert generally include a use of
> __FILE__ and __LINE__. Which also requires a macro,
>
> --
> James Kanze
Kindly give a code sample so that I can understand, regarding how to
turn off assert on a function by function basis.
Thanks
V.Subramanian
int f();
#include <assert.h>
assert(f() == 1); // assert active
#define NDEBUG
#include <assert.h>
assert(f() == 1); // assert not active
#undef NDEBUG
#include <assert.h>
assert(f() == 1); // assert active again
--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of
"The Standard C++ Library Extensions: a Tutorial and Reference"
(www.petebecker.com/tr1book)