
On Tue, Nov 8, 2011 at 2:07 PM, Dave Abrahams <dave@boostpro.com> 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