|
Boost : |
Subject: Re: [boost] [contract] Without the macros
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2016-06-24 17:43:47
2016-06-15 17:30 GMT+02:00 Lorenzo Caminiti <lorcaminiti_at_[hidden]>:
> Hello all,
>
> In the last few years my professional and personal lives changed quite
> a bit limiting my ability to contribute to Boost. However, in my spare
> time I slowing continued to revise Boost.Contract hoping to be able to
> commit it to a Boost release someday (after the library was accepted
> 3+ years ago...
> https://groups.google.com/forum/?fromgroups=#!topic/boost-list/jQ7OjAmos_Y
> ).
> While doing so I realized that leveraging C++11 lambda functions,
> Boost.Contract could be re-implemented without its crazy macros that
> alter C++ function declaration syntax.
>
> Boost.Contract still remains the only C++ library that implements
> *all* features of Contract Programming (a.k.a., Design by Contract or
> DbC): subcontracting, class invariants (also static and volatile),
> postconditions (with old and return values), preconditions,
> customizable actions on assertion failure (terminate, throw, etc.),
> optional assertion compilation, disable assertion checking while
> already checking other assertions (to avoid infinite recursion), etc.
> Furthermore, in its new incarnation Boost.Contract no longer uses
> crazy macros so it is easier to use, faster to compile, and gives
> readable compiler errors.
>
> For example:
>
> #include <boost/contract.hpp>
>
> int inc(int& x) {
> int result;
> boost::contract::old_ptr<int> old_x = BOOST_CONTRACT_OLDOF(x);
> boost::contract::guard c = boost::contract::function()
> .precondition([&] {
> BOOST_CONTRACT_ASSERT(x < std::numeric_limits<int>::max());
> })
> .postcondition([&] {
> BOOST_CONTRACT_ASSERT(x == *old_x + 1);
> BOOST_CONTRACT_ASSERT(result == *old_x);
> })
> ;
>
> return result = x++; // Function body.
> }
>
> Or with subcontracting:
>
> #include <boost/contract.hpp>
> #include <vector>
>
> template<typename T> class pushable; // Arbitrary base to demo
> subcontracting.
>
> template<typename T>
> class vector
> #define BASES public pushable<T>
> : BASES
> {
> public:
> typedef BOOST_CONTRACT_BASE_TYPES(BASES) base_types; //
> Subcontracting.
> #undef BASES
>
> void invariant() const { // Checked in AND with base class
> invariants.
> BOOST_CONTRACT_ASSERT(size() <= capacity()); // Line 25.
> }
>
> virtual void push_back(T const& value, boost::contract::virtual_*
> v = 0)
> /* override */ {
> boost::contract::old_ptr<unsigned> old_size =
> BOOST_CONTRACT_OLDOF(v, size()); // Old values.
> boost::contract::guard c = boost::contract::public_function<
> override_push_back>(v, &vector::push_back, this, value)
> .precondition([&] { // Checked in OR with base
> preconditions.
> BOOST_CONTRACT_ASSERT(size() < max_size()); // Line 35.
> })
> .postcondition([&] { // Checked in AND with base
> postconditions.
> BOOST_CONTRACT_ASSERT(size() == *old_size + 1); //
> Line 38.
> })
> ;
>
> vect_.push_back(value); // Function body.
> }
> BOOST_CONTRACT_OVERRIDE(push_back) // For `override_push_back`.
>
> // Could program contracts for those as well.
> unsigned size() const { return vect_.size(); }
> unsigned max_size() const { return vect_.max_size(); }
> unsigned capacity() const { return vect_.capacity(); }
>
> private:
> std::vector<T> vect_;
> };
>
> Full documentation and examples at:
> https://lcaminiti.github.io/boost-contract
>
> What do you think?
>
> Thanks,
> --Lorenzo
>
> P.S. The library can also be used without C++11 lambda functions (or
> any C++11 specific feature) but programmers have to write a fare
> amount of extra code to program the precondition and postcondition
> functors (using non-local functions, Boost.LocalFunction,
> Boost.Funsion, Boost.Lambda, or some other approach) so that might not
> useful in practice.
>
If I understand correctly, the change you propose moves the assertions from
function declaration into function definition. If this is the case, I would
say it is a step in wrong direction. Pre-/post-conditions are part of
function contract and belong in function declaration.
Also, from the example, it looks like I will have to pay for the existence
of `boost::contract::old_ptr` and `boost::contract::guard` (and compiler
warnings related to them) even if I disable contracts. Is that right? I do
not know what the previous macros did, but I had the impression that they
were able to erase any track of contract-related objects.
Also, the way you use the return value, forces me to depart from how I
normally write return statements.
While the new version is clearly more readable, it also has certain
important disadvantages compared to the previous version. What more do the
new version of pre/postconditions offers compared to putting two asserts at
the beginning and the end of the function? Preconditions are evaluated
after local object's destructors. What you propose is addressing the
subject from a different anle, but is not necessarily superior to the
previous version.
Regards,
&rzej
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk