Boost logo

Boost :

Subject: Re: [boost] [contract] Mixin Boost.Contract and Boost.STM
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2010-03-20 10:34:21


Hello:

On Tue, Mar 16, 2010 at 6:54 PM, vicente.botet <vicente.botet_at_[hidden]> wrote:
> I wanted to tell you that I wanted to mix Boost.Contract with Boost.STM. Software Transactional Memory needs already to

OK, once you start looking into it, please let me know. I will be
happy to help. I am not familiar with Boost.STM so I will take a look
at it in the meanwhile.

> As the copy is done always, we don't need any more to specify that we need to copy to be able to get the Old values as in

Note that in Boost.Contract not only the object but also the function
arguments can be tagged copyable. For example, if myvector was to have
a swap_back() operation, which swapped the vector's back with the
specified element, the contract could be written as follow:

    void swap_back(T& element)
    CONTRACT_FUNCTION( (class) (copyable)(myvector) // Copy object.
            (public) (void) (swap_back)( (copyable)(T&)(element) ) //
Copy argument.
    (postcondition) ({
        CONTRACT_ASSERT( back() == CONTRACT_OLDOF(element) );
        CONTRACT_ASSERT( element == CONTRACT_OLDOF(this)->back() );
    })
    (body) (
        ...
    ) )

Would Boost.STM allow to copy also the function arguments?

> BTW, does it means that CP can not be applied to non CopyConstructible classes?

Yes, doing the copies all the times would have the following plus and minus:
(+) The signature-sequence syntax will be simpler without the user
needing to specify (copyable) explicitly.
(-) Contracts can ONLY be written for objects and argument types that
can be copied -- even if the specific function contract does not use
OLDOF()...
(-) The run-time overhead of the copy operation will ALWAYS be present
-- even if the specific function contract does not use OLDOF()...

I think the last two minus would be major limitations for
Boost.Contract... hopefully, there is a way to use Boost.STM without
imposing these 2 key limitations on Boost.Contract...

> In addition Boost.STM works also for classes that are not CopyConstructible, but that follows the Shallow Copy
> semantics. This will allow to test for Old values on these kind of classes.

Boost.Contract now implements one of your previous suggestions to
relax the ConstCopyConstructible requirement. Boost.Contract copies a
type T via the following contract::copy:

    namespace contract {

    template<typename T>
    class copy: boost::noncopyable {
    private:
        typedef typename boost::add_const<typename
                boost::remove_reference<T>::type>::type const_type;
        typedef typename boost::add_reference<const_type>::type const_ref;

    public:
        const_type value;

        copy(const_ref source): value(source) {} // Use T's constant
copy constructor.
    };

    }

By default, this uses T's (public) constant copy constructor to make
the copy. However, programmers can specialize contract::copy for a
given type so to relax the ConstCopyConstructible requirement for that
type. For example
http://dbcpp.sourceforge.net/example/Mitchell2002/counter/decrement_button.hpp
allows to copy a type which is boost::noncopyable plus performs a deep
copy (instead of a shallow copy) of a member reference.

Vicente: I think this is inline with what you suggested me a few
months back. BTW, thanks a lot for the idea, it worked out quite
nicely!

> I have two questions. What (copyable) does on the constructor? Why it is not used on the destructor?

Constructors
(copyable) cannot be applied to the object for constructors
(Boost.Contract will generate a compile-time error otherwise). This is
because there is no object before body execution so "copy the object
before the body" does not make sense. Constructor arguments can still
be copied instead.

Destructor
(copyable) cannot be applied to the object for destructor
(Boost.Contract will generate a compile-time error otherwise). This is
because the destructor has no postconditions -- there are no
postconditions because there are no arguments and there is no object
after destructor body execution (the object has been destructed).

> I was wondering if instead of repeating the fuction signature and the class 'signature'
> you could be able to provide a macro CONTRACT_FUNCTION2 that allows to just write
> #define CONTRACT_CLASS (class) (myvector) (inherit)(pushable<T>)
>    CONTRACT_FUNCTION2(
>            ((public) (void) (push_back)( (const T&)(element) ))

I have experimented with this a similar idea in the past. However, I
concluded that copyable class type and inheritance both need to be
specified at the function-contract level (not at the class-contract
level) because:

1. Each function should be allowed to tag the class copyable because
one function might use OLDOF() in its postconditions while another
function might not.
The need for programmer to specify (copyable) could be removed but
only if I could metaprogram something like this "if
CONTRACT_OLDOF(variable-name) appears in this function postcondition
then replace variable-type with contract::copyable<variable-type>..."
-- I was not able to metaprogram something like that nor with the
Boost.Preprocessor, nor with Boost.MPL.

2. (inherit) also needs to be specified at the function level because
a specific function c::f() might override x::f() and y::f(), but
c::g() overrides z::g(), and maybe c::h() does not override -- where
x, y, and z are base classes of c. For a given function, the compiler
knows if that same function appears in one of the base classes but the
library does not. Therefore for a given function the programmers must
indicate the specific subset of base classes from which that function
is inherited from (and the library will subcontract from these
specific base functions).
Again, this could be avoided if I could metaprogram "for c::f(), if
x::f() exists (true) do something; or z::f() exists (false) do
something; ..." -- I was not able to metaprogram this.

One way I have investigated to omit the class type from
signature-sequence is to move it into the invariant:

    template<typename T>
    class myvector {

        CONTRACT_INVARIANT( (copyable)(myvector) ({ // Class type is here.
            ... // Invariants.
        }) )

    public:
        void push_back(const T& element)
        CONTRACT_FUNCTION( (inherit)(pushable<T>) // No class type.
                (public) (void) (push_back)( (const T&)(element) )
        ... )

        size_t size(void) const
        CONTRACT_FUNCTION( // No class type and no inherit here.
                (public) (void) (push_back)( (const T&)(element) )
        ... )
    };

Which could expand to something like this:

    template<typename T>
    class myvector {

        typedef contract::copyable<myvector> contract_class_type_; //
Expanded from INVARIANT().
        ... // The usual invariant check code.

    public:
        void push_back(const T& element)
        ... // The function contract check code now using
contract_class_type_ for the class type.

    };

Note:
1) There is no inherit for size() so (inherit) cannot be moved into
the invariant.
2) Now CONTRACT_FUNCTION() expansion will use contract_class_type_ for
the class type. But again, by doing so either all functions copy the
object or they do not regardless of whether a function actually uses
OLDOF(this) in its postcondition (and adding useless run-time overhead
for the unused OLDOF(this) copies).

Therefore I decided not to move the class type into the invariant so
to allow each function to selectively tag it (copyable) only in case
they need and use the object old value.

Regards,
Lorenzo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk