Exceptions vs Assertion for library API?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Exceptions vs Assertion for library API?

Boost - Dev mailing list
Any advice in choosing between throwing an exception vs asserting for
invalid uses of a library API?

I'm building a small "transactional" library I'd like to contribute to
boost  where certain API calls are constrained to only be valid if the
transaction is still "in-progress".  Such calls will never succeed, and are
easy for the implementation to diagnose.  I'd like to have the library fail
an assertion, which is far clearer to the user that the code logic is
incorrect, but it bothers me that assertions are much more difficult to
write unit tests for. (Is there even a concept of a test that passes if if
fails to compile or fails with an assertion in the boost testing framework?)

I suppose I could mangle the interface to ensure that the user has nothing
to invoke the API calls in question unless the transaction is in-progress,
but that generally requires a custom "lock" interface for my RAII
implementation that makes the API more complex to grasp.

--
Jon Biggar
[hidden email]

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Exceptions vs Assertion for library API?

Boost - Dev mailing list
On 10.11.2017 20:53, Jonathan Biggar via Boost wrote:
> Any advice in choosing between throwing an exception vs asserting for
> invalid uses of a library API?

Interesting question !

I'd strongly argue for assertions, against exceptions, in that case.

> I'm building a small "transactional" library I'd like to contribute to
> boost  where certain API calls are constrained to only be valid if the
> transaction is still "in-progress".  Such calls will never succeed, and are
> easy for the implementation to diagnose.

Now that is different: now the question is about behaviour, i.e. do you
want the library to report the error, or do you want to assume that the
input is valid ?
Assertions are useful if you allow different build variants, where a
"debug" variant can be used to validate application code against your
library, while a "release" variant simply assumes that the code uses
your library correctly. If you don't want to make that distinction, i.e.
always want to have a check, that would be an argument for exceptions.

>   I'd like to have the library fail
> an assertion, which is far clearer to the user that the code logic is
> incorrect, but it bothers me that assertions are much more difficult to
> write unit tests for. (Is there even a concept of a test that passes if if
> fails to compile or fails with an assertion in the boost testing framework?)

Yes, if you want to test the assertion of preconditions and invariants,
you can certainly do that. Make sure that an invalid argument to a
function call has the expected result, i.e. either an exception or an
abort. Of course, you wouldn't be able to test that in-process (as the
process would abort), but with a robust testing framework you can catch
aborts as valid process outcomes.

Of course, testing for raised exceptions is even easier, as they don't
require you to run testsĀ  in sub-processes.
> I suppose I could mangle the interface to ensure that the user has nothing
> to invoke the API calls in question unless the transaction is in-progress,
> but that generally requires a custom "lock" interface for my RAII
> implementation that makes the API more complex to grasp.

Regards,

Stefan

--

      ...ich hab' noch einen Koffer in Berlin...
   


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Exceptions vs Assertion for library API?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
AMDG

On 11/10/2017 06:53 PM, Jonathan Biggar via Boost wrote:

> Any advice in choosing between throwing an exception vs asserting for
> invalid uses of a library API?
>
> I'm building a small "transactional" library I'd like to contribute to
> boost  where certain API calls are constrained to only be valid if the
> transaction is still "in-progress".  Such calls will never succeed, and are
> easy for the implementation to diagnose.  I'd like to have the library fail
> an assertion, which is far clearer to the user that the code logic is
> incorrect, but it bothers me that assertions are much more difficult to
> write unit tests for. (Is there even a concept of a test that passes if if
> fails to compile or fails with an assertion in the boost testing framework?)
>

  You should not define the API as asserting.  Instead,
you should say that if the preconditions are not met,
the behavior is undefined.  Then you can implement it
using an assertion for normal use, with the ability to
switch to an exception for testing.

> I suppose I could mangle the interface to ensure that the user has nothing
> to invoke the API calls in question unless the transaction is in-progress,
> but that generally requires a custom "lock" interface for my RAII
> implementation that makes the API more complex to grasp.
>

  It's hard to say, without actually seeing the API in
question, but when possible, it's usually better to
design APIs that cannot be misused rather than relying
on the user to make calls in the correct sequence.

In Christ,
Steven Watanabe


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Exceptions vs Assertion for library API?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
Jonathan Biggar wrote:

> I'd like to have the library fail an assertion, which is far clearer to
> the user that the code logic is incorrect, but it bothers me that
> assertions are much more difficult to write unit tests for. (Is there even
> a concept of a test that passes if if fails to compile or fails with an
> assertion in the boost testing framework?)

Easiest is probably to just use a run-fail test. A failed assertion exits
the process with a nonzero code.

Or, you can define BOOST_ENABLE_ASSERT_HANDLER and do whatever you like in
your assertion handler, including throwing an exception that you can then
test with BOOST_TEST_THROWS, but this requires your functions to not be
noexcept. If they are, you either need to conditionally remove the noexcept
from them when some "test mode" macro is defined, or do something like

#include <boost/config.hpp>
#undef BOOST_NOEXCEPT
#define BOOST_NOEXCEPT
#include <boost/mylib/myheader.hpp>

References:

http://www.boost.org/doc/libs/1_65_1/libs/assert/doc/html/assert.html
http://www.boost.org/doc/libs/1_65_1/libs/core/doc/html/core/lightweight_test.html#core.lightweight_test.header_boost_core_lightweight_te.boost_test_throws


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Exceptions vs Assertion for library API?

Boost - Dev mailing list
In reply to this post by Boost - Dev mailing list
Hi,

Am 11.11.2017 02:53, schrieb Jonathan Biggar via Boost:
> I'm building a small "transactional" library I'd like to contribute to
> boost  where certain API calls are constrained to only be valid if the
> transaction is still "in-progress".

I think there is some room of improvement on the API, whenever an issue
like that arises. For a transactional library I'd try and create an
interface, where the lifetime of the transaction object is in line with
the transaction being "in progress". The destructor would simply roll
back, if the transaction has not been moved from, while a commit()
function that takes an rvalue reference to a transaction commits it. The
compiler would warn, if the transaction object is used after it has been
moved from.

Something like this:

----
// begin transaction
auto t = transaction{transactional_memory};

// do something
auto bar = t.find_foo("bar");
// might throw
bar.extend("blabla");

// will commit if extend() did not throw, rollback otherwise.
// Note, that t is moved into the parameter of commit here.
commit(t);

// undefined behaviour, compiler will warn.
auto lala = t.find_foo("lala");

// will do move assignment. t is usable again,
// but now refers to the other transaction.
t = get_another_transaction_somehow();
// now this is safe, but will of course use the new transaction.
auto lala = t.find_foo("lala");
----

But maybe I just misunderstood, what you are trying to do.

Christof

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost