Boost logo

Boost :

Subject: Re: [boost] [contract] syntax redesign
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2011-11-08 15:30:21


On Tue, Nov 8, 2011 at 2:07 PM, Dave Abrahams <dave_at_[hidden]> wrote:
>
> on Tue Nov 08 2011, Lorenzo Caminiti <lorcaminiti-AT-gmail.com> wrote:
>
>> The following turned out to be a decent example that uses together a
>> bit of all the different features (contracts, concepts, and named
>> parameters) so I decided to cut-n-paste it here in an email:
>>
>> #include <contract.hpp>
>> #include <boost/concept_check.hpp>
>> #include <boost/type_traits/has_equal_to.hpp>
>> #include <boost/mpl/placeholders.hpp>
>> #include <boost/mpl/int.hpp>
>> #include <boost/mpl/long.hpp>
>> #include <boost/mpl/char.hpp>
>> #include <iostream>
>>
>> namespace num
>> {
>>
>> namespace tag // Namespace `tag` already used here, use `keywords` instead.
>> {
>>     typedef int Value;                      // Dummy declarations...
>>     typedef boost::mpl::int_<1> Default;
>>     Value value = Default::value;
>> }
>
> The point of the above is just to have an excuse to use the "namespace"
> option below?  I would leave this out of the example (and leave out that
> option); it's not useful to show corner-case handling like this in early
> examples.

Yes, it's just an excuse to use a namespace different than tag. I am
now using these examples to design the library so I want them to
exercise all the different features. In the library docs, I will
probably use different/modified examples and I will make sure to not
confuse the users with the namespace stuff (I'll only mention it in
the Advanced Section).

>> // Also use `Param` and `_param` postfixes instead of `_` prefix.
>> CONTRACT_TEMPLATE_PARAMETER((ValueParam, keywords) Value)
>> CONTRACT_TEMPLATE_PARAMETER((DefaultParam, keywords) Default)
>> CONTRACT_PARAMETER((value_param, keywords) value)
>
> Ditto, I think.

Yes, same as for namespace (there to exercise the feature and I'll
document it only for advanced usages).

>> CONTRACT_CLASS(
>> template( // Named parameters.
>>     namespace keywords, // Use namespace `keywords` instead of `tag`.
>>     in typename Value, default int,
>
> I presume this is essentially "typename Value=int".  I don't love
> separating one argument with a comma that way.  Would
>
>  in typename (Value, int)
> or something similar be practical?

No because my syntax also supports non-variadic compilers and the
above will have to become (Value)(int) which forces extra parenthesis
around (Value) even when there is no default. `..., default ...` is
consistently used to indicate default parameter value (template or
functional) and in my experience you get used to it pretty quickly. In
fact, I personally prefer Value, default int to (Value, int) (because
in the second there is no description of the semantic of the 2nd token
int).

>>     in requires(is_same<boost::mpl::_::type, Value>) Default,
>>             default boost::mpl::int_<1>
>
>
> I don't understand what this is supposed to be saying.  Could you
> explain it?

This is a way of passing a named value template parameter by wrapping
within a type template parameter as you suggested... is there a better
way to do this? I want to pass:

template< typename Value, Value default_value = 1 >

Where both Value and default_value are named using Boost.Parameter.

> Have you thought of using decltype in the implementation, to avoid
> writing ::type up there?

I'm not using C++1 features. (The C++11-looking auto, static_assert,
etc that you see in Boost.Contract are all handled by the pp and
implemented using C++03.)

> Something like boost::mpl::_::type seems like maybe it should be spelled
> "auto" (if possible).

It cannot because it is nested inside the named parameter type
predicate so the pp has to way to parse it. ::type is needed because I
am using a type to wrap a value so the value template parameter can be
named (as indicated above).

>> ) requires( boost::Copyable<Value> ) // Concepts.
>> struct (positive)
>> ) {
>>     CONTRACT_CLASS_INVARIANT_TPL( // Contracts.
>
> Why is that a better spelling than CONTRACT_CLASS_TPL_INVARIANT?
>
>>         static class(
>>             static_assert(Default::value > 0, "positive default value")
>>         ),
>
> I don't understand what the "static class(...)" construct is doing here.

This will be detailed in Boost.Contract docs (it's an addition over
N1962). All assertions within static class(...) specify /static class
invariants/. (Non-static) class invariants are not checked at
entry/exit of static functions or at constructor entry. However,
static class invariants are also checked at entry/exit of static
functions and at constructor entry. Obviously, static class invaraints
cannot refer to the object (but only to static member data/functions).
In addition, this one static class invariant is using a static
assertion (for this example, given that the assertion is static it
could also have been programmed as part of the non-static class
invariants and get the same type of checking which is always at
compile-time for static_assert). Static class invariants were
discussed with N1962 authors 1+ years ago over the Boost ML (no one
had objections on them but no one seemed to think they will be useful
in real life).

>>         get() > 0 )
>>
>>     CONTRACT_CONSTRUCTOR_TPL(
>>     public (positive) ( void )
>
> Is "void" mandatory here?

No if your compiler supports empty macro parameters or variadics.
However, often MSVC (which in theory supports both) gets confused and
generates pp-error. So I always use void to indicate and empty
(parameter) list so the code is most portable.

>>         initialize( value_(Default::value) ) ) {}
>
> What is "value_"?

A private member variable.

>>     CONTRACT_FUNCTION_TPL(
>>     public void (set) ( namespace keywords, in (Value const&) value )
>>         precondition( value > 0 )
>>         postcondition(
>>             get() == value, requires boost::has_equal_to<Value>::value
>
> Why must ::value appear in the previous line?

Because assertion requirements are specified using integral static
constants and not nullary metafunctions. This could be (esaily)
changed to be more consistent with named parameter requires that uses
unary metafunctions... I'll think about it.

>>         )
>>     ) {
>>         value_ = value;
>>     }
>>
>>     CONTRACT_FUNCTION_TPL(
>>     public (Value const&) (get) ( void ) const
>>     ) {
>>         return value_;
>>     }
>>
>> private:
>>     Value value_;
>> };
>>
>> } // namespace num
>>
>> int main ( void )
>> {
>>     num::positive< char, boost::mpl::char_<'z'> > p;
>>     num::positive< > q;
>>
>>     // Use `...Param` instead of `_...`.
>>     num::positive< num::DefaultParam< boost::mpl::int_<10> > > r;
>>     num::positive<
>>           num::DefaultParam< boost::mpl::long_<10> >
>>         , num::ValueParam<long>
>>     > s;
>>
>>     std::cout << s.get() << std::endl;
>>     s.set(num::value_param = 123); // Use `..._param` instead of `_...`.
>>     std::cout << s.get() << std::endl;
>>
>>     return 0;
>> }
>>
>> The goal here is to use Boost.Contract to /completely/ specify the
>> interface for the positive abstract data type.
>
> This is very nice, but still could use some explanation and
> simplification.

Explanation will definitely go in the docs. What simplifications would
you suggest?

Thanks a lot :)
--Lorenzo


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