Boost logo

Boost :

Subject: Re: [boost] [contract] syntax redesign
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-09-04 18:35:17


On Fri, Sep 2, 2011 at 5:45 PM, Andrzej Krzemienski <akrzemi1_at_[hidden]> wrote:
>> Comments are always welcome :)
>
> Hi Lorenzo,
> The number of examples is impressive. It is a pleasure to observe your
> library growing at this pace. I would like to suggest two things. I could
> not see from the examples, if the library is capable of supporting "function
> try blocks" in constructor and destructor definitions. This is what I mean.
> http://msdn.microsoft.com/en-us/library/e9etx778%28v=vs.71%29.aspx
> http://www.gotw.ca/gotw/066.htm

I have added this to my TO-DO. Currently this is not possible and I
don't know if I can implement it but I will look into it (it's yet
another good suggestion you made, thanks). The point is that my macros
can essentially expand to any code that might be needed for this but
how do I implement this in C++ even if I am willing to program (or
have the macros expand to) a lot of boiler-plate code? I need to look
into what can be done.

> Second, I have seen #ifndef pragmas in some of the examples, e.g., in the
> body of function myadvance_dispatch you have:
>
>    {
> #ifndef CONTRACT_CONFIG_NO_LOOP_VARIANTS
>        Distance n_max = n;
> #endif
>        CONTRACT_LOOP( while(n++) ) {
>            CONTRACT_LOOP_VARIANT( const( n_max, n ) n_max - n )
>            --i;
>        }
>    }
>
> I am not sure if the necessity to use this conditional code would occur
> often, but if so, perhaps the code would be slightly more clear if instead
> the conditional code would be wrapped in an additional macro, like MFC did
> with DEBUG_ONLY macro:
>
>    {
>        CONTRACT_CONFIG_LOOP_VARIANT_ONLY( Distance n_max = n );
>
>        CONTRACT_LOOP( while(n++) ) {
>            CONTRACT_LOOP_VARIANT( const( n_max, n ) n_max - n )
>            --i;
>        }
>    }

In this case the conditional declaration of n_max is to avoid an
"unused variable" warning when loop variants are disabled. However,
you can simply program this differently:

{
    Distance m = n;
    CONTRACT_LOOP( while(m++) ) {
        CONTRACT_LOOP_VARIANT( const( n, m ) n - m )
        --i;
    }
}

or accept the warning, or use the pragma. This use case is really a
programmers' choice and I wanted to show that using the pragma is one
of the options (I will explain this in the docs).

> That is, macro CONTRACT_CONFIG_LOOP_VARIANT_ONLY is defined as:
>
>  #ifndef CONTRACT_CONFIG_NO_LOOP_VARIANTS
>  #define CONTRACT_CONFIG_LOOP_VARIANT_ONLY(ARG) ARG
>  #else
>  #define CONTRACT_CONFIG_LOOP_VARIANT_ONLY(ARG)
>  #endif
>
> Although I am not sure if a similar trick would be possible with the
> definition like:
>
>    CONTRACT_FUNCTION(
>    template( typename T )
>        requires(
>            Addable<T>
>    #ifndef CONTRACT_CONFIG_NO_POSTCONDITIONS // Equality only for
> postconditions.
>            , boost::EqualityComparable<T>
>    #endif
>        )
>    (T) (add) ( (T) x, (T) y )
>        postcondition( auto result = return, result == x + y )
>    ) {
>        return x + y;
>    }

This use case is different then the above (more compelling). The
issues here is that when programming contracts you quickly start using
extra checks (and especially ==) which add additional requirements on
generic types. For example, in this case T does not have to be
EqualityComparable to program the body but you still want to program
and check the postcondition when T happens to be EqualityComparable. I
have been thinking to solve this issue by providing:

namespace contract {

temlate< typename T>
bool equal(T const& left, T const& right) ...

}

equal will return true if T is not EqualityComparable and left ==
right if T is EqualityComparable. That way you can program the
contracts using contract::equal(x, y) without introducing additional
requirements on T at all but still checking the contract conditions
for EqualityComparable types. I will also expand equal to handle
containers (compare all elements of a vector, etc) and I will provide
similar functions for other operations. All of these contract helper
functions will be in a separate header <contract/utility.hpp>. I have
not started to implement this yet.

BTW, I am also wondering if it would be useful to implement a fully
contracted version of the STL probably in contract::std:

#include <contract/std/vector.hpp>
#include <contract/std/algorithm.hpp>

contract::std::vector<int> v;
contract::std::for_each(v.begin(), v.end(), ...);

I think Boost.Contract can essentially implement most/all of the
interface contracts (preconditions, postconditions, invariants, and
concepts) documented by the SGI:
http://www.sgi.com/tech/stl/

Would such a contract::std be any useful?

Thanks a lot for your comments!
--Lorenzo


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