[assert] Assert library? - Was: [regression] Where's boost/assert.hpp ??

On Sat, Feb 22, 2014 at 9:39 PM, Andrey Semashev
It's been moved to a new Boost.Assert library (libs/assert submodule).
Is there a discussion or documentation about what is this new library exactly? Is it just to isolate the assert.hpp header or are there other plans? (I'm secretly hoping that someone would propose an implementation of n3877[1] to boost, as I don't have the time to do it myself) [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3877.pdf

On Sat, Feb 22, 2014 at 6:08 PM, Klaim - Joël Lamotte
Is there a discussion or documentation about what is this new library exactly?
Yes; see: http://lists.boost.org/Archives/boost/2013/12/209586.php

On 23/02/14 03:08, Klaim - Joël Lamotte wrote:
On Sat, Feb 22, 2014 at 9:39 PM, Andrey Semashev
wrote: It's been moved to a new Boost.Assert library (libs/assert submodule).
Is there a discussion or documentation about what is this new library exactly? Is it just to isolate the assert.hpp header or are there other plans? (I'm secretly hoping that someone would propose an implementation of n3877[1] to boost, as I don't have the time to do it myself)
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3877.pdf
That's a long paper to say "we should have three levels of asserts". I think it would be more interesting to have another API that is more scalable if you ever feel like having more levels. In any case, at the moment Boost.Assert is quite messy in the way that it handles flags that enable or disable its behaviour. I filed bugs to suggest fixing it, but it wasn't well received. Apparently people prefer having plenty of different macros which have non-obvious interactions with each other instead of a single macro that always does what's asked.

Mathias Gaunard wrote:
Apparently people prefer having plenty of different macros which have non-obvious interactions with each other instead of a single macro that always does what's asked.
In what way do the current macros not do what's asked? What would you like to see instead?

On 23/02/14 15:29, Peter Dimov wrote:
Mathias Gaunard wrote:
Apparently people prefer having plenty of different macros which have non-obvious interactions with each other instead of a single macro that always does what's asked.
In what way do the current macros not do what's asked? What would you like to see instead?
Try to answer the following questions: - what's the difference between BOOST_DISABLE_ASSERTS and NDEBUG? - does defining BOOST_ENABLE_ASSERT_HANDLER affect whether assertions are enabled or not? - what's the difference between BOOST_ASSERT and BOOST_ASSERT_MSG The answers one would expect are: - BOOST_DISABLE_ASSERTS disables all assertions done with Boost.Assert, while NDEBUG disables all assertions done with Boost.Assert or standard assert. - No, defining a handler is orthogonal to whether assertions are enabled or not - The only difference is that _MSG allows to define a message. Unfortunately, none of those answers are true: defining NDEBUG does not disable BOOST_ASSERT (but it does disable BOOST_ASSERT_MSG -- nice inconsistency) if BOOST_ENABLE_ASSERT_HANDLER is set.

Mathias Gaunard wrote:
Unfortunately, none of those answers are true: defining NDEBUG does not disable BOOST_ASSERT (but it does disable BOOST_ASSERT_MSG -- nice inconsistency) if BOOST_ENABLE_ASSERT_HANDLER is set.

On 24/02/14 01:41, Peter Dimov wrote:
Mathias Gaunard wrote:
Unfortunately, none of those answers are true: defining NDEBUG does not disable BOOST_ASSERT (but it does disable BOOST_ASSERT_MSG -- nice inconsistency) if BOOST_ENABLE_ASSERT_HANDLER is set.
That doesn't change the fact that: - NDEBUG and BOOST_DISABLE_ASSERTS are inconsistent - BOOST_ASSERT and BOOST_ASSERT_MSG are inconsistent

Mathias Gaunard wrote:
That doesn't change the fact that: - NDEBUG and BOOST_DISABLE_ASSERTS are inconsistent
If by "inconsistent" you mean that they don't do the same thing, then yes. If they did, there would have been no need to have BOOST_DISABLE_ASSERTS. BOOST_DISABLE_ASSERTS disables BOOST_ASSERT(_MSG). I don't know how it can be made more consistent than that.
- BOOST_ASSERT and BOOST_ASSERT_MSG are inconsistent
No.

On 24/02/14 12:59, Peter Dimov wrote:
Mathias Gaunard wrote:
That doesn't change the fact that: - NDEBUG and BOOST_DISABLE_ASSERTS are inconsistent
If by "inconsistent" you mean that they don't do the same thing, then yes. If they did, there would have been no need to have BOOST_DISABLE_ASSERTS.
From what I would have expected, BOOST_DISABLE_ASSERTS disables boost asserts without disabling standard asserts. This is a useful feature to have, and it is enough to justify a need for the macro. Now you may want -DNDEBUG -DBOOST_ENABLE_ASSERT_HANDLER to still keep Boost assertions but disable standard assertions. That's a valid option, but it should be done consistenly.
BOOST_DISABLE_ASSERTS disables BOOST_ASSERT(_MSG). I don't know how it can be made more consistent than that.
- BOOST_ASSERT and BOOST_ASSERT_MSG are inconsistent
No.
With master, -DNEBUG -DBOOST_ENABLE_ASSERT_HANDLER will disable BOOST_ASSERT_MSG, but not BOOST_ASSERT. I see the code has changed on develop, I haven't looked at it yet, hopefully it's fixed.

On Sun, Feb 23, 2014 at 1:58 PM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
That's a long paper to say "we should have three levels of asserts".
Actually, no, it's more like "there is, in practice, only 3 way to manage asserts", which is explained this long certainly because it's not that obvious. In my experience, I've been using similar custom libraries than what is proposed in this paper for a long time so I believe it's 1. correct 2. useful 3. a good deep analysis. Also, I belive that the fact that Bloomberg use this library internally and have been doing so for a lot of time makes their point have weight. (and it matches my experience, whatever the kind of projects I've been working on actually, as long as it's in C++)
I think it would be more interesting to have another API that is more scalable if you ever feel like having more levels.
I believe that there is no other "levels". By the way, one of the points is to let the library user provide an application-specific reaction to assertions if he/she wants. What more liberty than that would you want? Or maybe you have other ideas of how such library could be designed?

On 23/02/14 17:01, Klaim - Joël Lamotte wrote:
On Sun, Feb 23, 2014 at 1:58 PM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
That's a long paper to say "we should have three levels of asserts".
Actually, no, it's more like "there is, in practice, only 3 way to manage asserts", which is explained this long certainly because it's not that obvious. In my experience, I've been using similar custom libraries than what is proposed in this paper for a long time so I believe it's 1. correct 2. useful 3. a good deep analysis.
The paper says that whether enabling some checks or not should be decided based on desired performance. It claims debug with no asserts is 20% slower than release and that debug with asserts is 300% slower than release. Not only is that highly variable (it's possible to have programs that are 100 slower in debug mode even without asserts), but it's probably not necessarily a good criterion, as it should be up to the user of the software component to decide what level of checking he wants depending on what he's doing with it rather than having a standard "build mode" mindset. Doing additional checking usually requires to store more information for debugging purposes, and that affects binary compatibility. This is the line that people usually use to choose what's enabled by default and what isn't.
Also, I belive that the fact that Bloomberg use this library internally and have been doing so for a lot of time makes their point have weight.
It has a race in the TEST_ macros. If the handler is changed in the middle of the invocation of such a test, then the change gets discarded. Being used by a big company doesn't necessarily make something bug-free.
I believe that there is no other "levels".
In practice, that's not quite what you want. You may have different components that make up your application, each of which can use asserts to validate that the input matches its contract. What you may want is to enable asserts on only parts of the application because you have already validated that your use of a certain component is correct.
What more liberty than that would you want? Or maybe you have other ideas of how such library could be designed?
Make the level an argument of the macros instead of duplicating all macros three times.

On Mon, Feb 24, 2014 at 12:10 AM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
On 23/02/14 17:01, Klaim - Joël Lamotte wrote:
On Sun, Feb 23, 2014 at 1:58 PM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
That's a long paper to say "we should have three levels of asserts".
Actually, no, it's more like "there is, in practice, only 3 way to manage asserts", which is explained this long certainly because it's not that obvious. In my experience, I've been using similar custom libraries than what is proposed in this paper for a long time so I believe it's 1. correct 2. useful 3. a good deep analysis.
The paper says that whether enabling some checks or not should be decided based on desired performance. It claims debug with no asserts is 20% slower than release and that debug with asserts is 300% slower than release.
Not only is that highly variable (it's possible to have programs that are 100 slower in debug mode even without asserts), but it's probably not necessarily a good criterion, as it should be up to the user of the software component to decide what level of checking he wants depending on what he's doing with it rather than having a standard "build mode" mindset.
My understanding was that it was the actual point of the paper?
Doing additional checking usually requires to store more information for debugging purposes, and that affects binary compatibility. This is the line that people usually use to choose what's enabled by default and what isn't.
Indeed.
Also, I belive that the fact that Bloomberg use this library internally
and have been doing so for a lot of time makes their point have weight.
It has a race in the TEST_ macros. If the handler is changed in the middle of the invocation of such a test, then the change gets discarded. Being used by a big company doesn't necessarily make something bug-free.
Of course it's not bug-free, it's not what I was saying either. I was pointing credibility from my personal perspective. Most Boost libraries are full of bugs, they are still high standard. Anyway...
I believe that there is no other "levels".
In practice, that's not quite what you want.
I would say: that's the minimum of what I want.
You may have different components that make up your application, each of which can use asserts to validate that the input matches its contract. What you may want is to enable asserts on only parts of the application because you have already validated that your use of a certain component is correct.
This would be an interesting feature (per-library behaviour). However I fail to see that would be implemented in a generic way. Or do you mean using test macro arguments as library identifiers?
What more liberty than that would
you want? Or maybe you have other ideas of how such library could be designed?
Make the level an argument of the macros instead of duplicating all macros three times.
It's not clear to me if you mean that doing this change would help having per-library assertion behaviour, or if you're just direcly answering my questions in an unrelated way?

Klaim - Joël Lamotte wrote:
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3877.pdf
Assertion macros taking a level (BOOST_ASSERTL, BOOST_ASSERTL_MSG) controlled by, f.ex. BOOST_ASSERT_LEVEL (0 - opt, 1 - debug, 2 - safe) would be a relatively straightforward addition. The rest is not really in the spirit of the current Boost.Assert. It could be added though. I'm not entirely sure about the purpose of the test macros.

On 24/02/14 15:33, Peter Dimov wrote:
Klaim - Joël Lamotte wrote:
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3877.pdf
Assertion macros taking a level (BOOST_ASSERTL, BOOST_ASSERTL_MSG) controlled by, f.ex. BOOST_ASSERT_LEVEL (0 - opt, 1 - debug, 2 - safe) would be a relatively straightforward addition. The rest is not really in the spirit of the current Boost.Assert. It could be added though.
I'm not entirely sure about the purpose of the test macros.
The main difference with Boost.Assert is that the interface allows to change the handler at runtime. By changing it at runtime, it is possible to temporarily make the handler throw exceptions, which is how the TEST_ macros are implemented. They're useful if you want to write unit tests that checks that your functions do indeed emit an assert in a situation that is invalid.

Mathias Gaunard wrote:
They're useful if you want to write unit tests that checks that your functions do indeed emit an assert in a situation that is invalid.
Asserting that something should assert, how very Lakos. The function checking its own contract is itself part of a contract.
The main difference with Boost.Assert is that the interface allows to change the handler at runtime.
I'm of two minds about that feature. Either way, it's not hard to write boost::assertion_failed that calls a violation handler, so this is certainly implementable on top of the current Boost.Assert. But providing a sensible - consistent, you'd say - interface to both the old functionality and the new one (if it's added) might prove a challenge. Originally BOOST_ASSERT was motivated by the fact that many Boost libraries used 'assert', which generally did not offer enough control for users (of said Boost libraries - not of BOOST_ASSERT which didn't exist yet). And it being 'assert' by default was intended to make a search and replace of assert with BOOST_ASSERT a risk-free procedure. But then people started wanting more, and sometimes less. The standard 'assert', say, brings up a dialog box, and we want just simple std::cerr output. But with a message. And sometimes we actually want std::cout output. Or stdout, when iostreams are not available. And so on. The idea was that everyone who wants something not-'assert' was free to write a custom handler, but that's not how it panned out. One way forward would probably be to just cut all ties with the standard assert macro and go our own way. Not sure how many people this would upset.

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3877.pdf
Assertion macros taking a level (BOOST_ASSERTL, BOOST_ASSERTL_MSG) controlled by, f.ex. BOOST_ASSERT_LEVEL (0 - opt, 1 - debug, 2 - safe) would be a relatively straightforward addition.
The N3877 approach of supplying three separate macros, one per level, has one advantage though - it doesn't generate "condition is always true/false" warnings. Assume for instance that BOOST_ASSERTL(expr, level) is ((level) <= BOOST_ASSERT_LEVEL? BOOST_ASSERT(expr): (void)0) Here level <= BOOST_ASSERT_LEVEL will almost always be a constant expression, hence warnings.

On Saturday 01 March 2014 02:43:35 Peter Dimov wrote:
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3877.pdf
Assertion macros taking a level (BOOST_ASSERTL, BOOST_ASSERTL_MSG) controlled by, f.ex. BOOST_ASSERT_LEVEL (0 - opt, 1 - debug, 2 - safe) would be a relatively straightforward addition.
The N3877 approach of supplying three separate macros, one per level, has one advantage though - it doesn't generate "condition is always true/false" warnings.
Assume for instance that BOOST_ASSERTL(expr, level) is
((level) <= BOOST_ASSERT_LEVEL? BOOST_ASSERT(expr): (void)0)
Here level <= BOOST_ASSERT_LEVEL will almost always be a constant expression, hence warnings.
Leaving the note about usefulness of this warning aside, you could work around it e.g. by using preprocessor tricks: #define BOOST_ASSERTL(expr, level)\ BOOST_ASSERTL_ ## level(expr) or template specialization: #define BOOST_ASSERTL(expr, level)\ assert_impl< (level) <= BOOST_ASSERT_LEVEL >::do((expr), #expr, __FILE__, __LINE__) Of course, this would only work for compile time constant levels. The three- macros approach guarantees that so it might cause less surprises than the level-as-argument approach.

Andrey Semashev wrote:
Leaving the note about usefulness of this warning aside, you could work around it e.g. by using preprocessor tricks:
#define BOOST_ASSERTL(expr, level)\ BOOST_ASSERTL_ ## level(expr)
I'd thought of that. But it works only if level is a number token or a macro expanding to a number token, and the implementation can no longer not care what the levels are, it has to list them as BOOST_ASSERTL_0 and so on.
or template specialization:
#define BOOST_ASSERTL(expr, level)\ assert_impl< (level) <= BOOST_ASSERT_LEVEL >::do((expr), #expr, __FILE__, __LINE__)
This will still evaluate 'expr'.
participants (5)
-
Andrey Semashev
-
Glen Fernandes
-
Klaim - Joël Lamotte
-
Mathias Gaunard
-
Peter Dimov