[contract] syntax redesign

classic Classic list List threaded Threaded
52 messages Options
123
Reply | Threaded
Open this post in threaded view
|

[contract] syntax redesign

lcaminiti
Hello all,

After a long time of inactivity for Boost.Contract, I am considering
how to re-design its macro syntax to make it simpler.

I can eliminate a large number of extra parenthesis based on a few pp
tricks I've learned in processing Boost.Local's `const bind(type)&
var`. Plus, I can provide (optional) support for variadics macros so
to use comma-separated pp tuplets instead of parenthesized pp
sequences on preprocessors with variadics.

For example, I _think_ (but I've not implemented this yet) that I can
simplify the syntax like this:

#include <contract.hpp>
#include <vector>
#include "myvector/pushable.hpp"

// Wrapper class that adds contracts to std::vector.
CONTRACT_CLASS(
template( typename T )
class (myvector) extends( public pushable<T> ) // Subcontracting.
) {
    CONTRACT_CLASS_INVARIANT(
        empty() == (size() == 0)
        // More invariants here (comma separated)...
    )

    CONTRACT_FUNCTION(
    public void (push_back)( (const T&) element )
        precondition(
            size() < max_size()
            // More preconditions here (comma separated)...
        )
        postcondition(
            auto old_size = CONTRACT_OLDOF(size()), // Old value.
            size() == old_size + 1
            // More postconditions here (comma separated)...
        )
    ) {
        vector_.push_back(element); // Implementation.
    }

    // Rest of the class here (with more contracts if needed)...
public:
    typedef typename std::vector<T>::size_type size_type;
    size_type size(void) const { return vector_.size(); }
    size_type max_size(void) const { return vector_.max_size(); }
    bool empty(void) const { return vector_.empty(); }
    const T& back(void) const { return vector_.back(); }
private:
    std::vector<T> vector_;
};

For a side-by-side comparison with N1962 syntax:
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html/index.html#contract__.introduction.an_example
For a comparison of the entire std::vector (plus Boost.ConceptCheck concepts):
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html/contract__/examples.html
For a comparison for all Boost.Contract features (plus
Boost.ConceptCheck concepts and Boost.Parameter named parameters):
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html/contract__/tutorial.html#contract__.tutorial.a_fully_working_example
(Don't pay attention to the rest of the docs that are way out of date.)

A few notes:
* Extra parenthesis are still needed around class and function names.
* Extra parenthesis are still needed around types (like for the
parameter `(T) element`) but only if the type is not a C++ keyword (no
parenthesis needed aournd int, double, void, etc) or it contains a
symbol (parenthesis needed if & or * are used `(int&) x`, etc).
* In postconditions, the `auto old_size = CONTRACT_OLDOF(size())` only
copies the vector size (and not the entire vector :)) ). Similarly,
`auto result = return` can be used to access the return value for
non-void functions. (The `auto ...` is parsed by the pp and it's
implemented using Boost.Typeof so no C++0x is required.)
* Template declaration (but not instantiation) angular parenthesis <>
are replaced by round parenthesis ().
All of this _should_ be implementable in pure ISO C++.

What do you think? Based on the discontent that the previous
"parenthesized" syntax generated among Boosters, the key question is:
Would you consider this new syntax usable??

Thanks,
--Lorenzo
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Tue, May 31, 2011 at 2:49 PM, Lorenzo Caminiti <[hidden email]>wrote:
>
> For example, I _think_ (but I've not implemented this yet) that I can
> simplify the syntax like this:
>

Looks good, I use pretty much exactly the same syntax in my library
concerning parameter lists and templates (
https://github.com/boostcon/2011_presentations/raw/master/thu/Boost.Generic.pdf
slides
79 and 96 are okay examples). The only thing I'm confused about is how you
specify the return type of your CONTRACT_FUNCTION -- in particular, you did
not wrap it in parentheses. Are you really able to do that, or is that a
mistake? If that syntax is indeed possible, then I'd like to see the
implementation because I too would love to be able to rid myself of some
parentheses.

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
Matt Calabrese wrote
On Tue, May 31, 2011 at 2:49 PM, Lorenzo Caminiti <[hidden email]>wrote:
>
> For example, I _think_ (but I've not implemented this yet) that I can
> simplify the syntax like this:
>

Looks good, I use pretty much exactly the same syntax in my library
concerning parameter lists and templates (
https://github.com/boostcon/2011_presentations/raw/master/thu/Boost.Generic.pdf
slides
79 and 96 are okay examples). The only thing I'm confused about is how you
Thanks, I'll take a look.

specify the return type of your CONTRACT_FUNCTION -- in particular, you did
not wrap it in parentheses. Are you really able to do that, or is that a
If the return type is a "keyword" known a priori like void then no parenthesis should be needed. I'd parse it using the pp PP_IS_UNARY to first check if there are parenthesis. If there are no parenthesis then I'd check if the token is a known "keyword" type `void`, `int`, `double`, etc using macros like BOOST_DETAIL_PP_KEYWORD_IS_VOID_FRONT and strip it aways from the front using macros like BOOST_DETAIL_PP_KEYWORD_VOID_REMOVE_FRONT (see http://svn.boost.org/svn/boost/sandbox/local/boost/detail/preprocessor/keyword/).

So, this does not need the parenthesis because int is a "keyword" known a priory (void return type also belongs to this category):

    CONTRACT_FUNCTION( public int (size)() ... )

This instead needs the parenthesis because my_own_size_type is a user-defined type and it's not known a priory:

    CONTRACT_FUNCTION( public (my_own_size_type) (size)() ... )

Also this needs the parenthesis because int* contains a non-alphanumeric symbol * that cannot be handled by the BOOST_DETAIL_PP_KEYWORD_XXX macros (because they use pp concatenation):

    CONTRACT_FUNCTION( public (int*) (size)() ... )

The same applies to all specified types including function parameter types `void (f)( int x, int y )` vs. `void (f)( (int*) x, int y )`.

mistake? If that syntax is indeed possible, then I'd like to see the
implementation because I too would love to be able to rid myself of some
parentheses.
I have done some experimentation that indicates this syntax _should_ be possible but it's not actually implemented yet so... we'll see :)

--Lorenzo
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Tue, May 31, 2011 at 5:21 PM, lcaminiti <[hidden email]> wrote:
>
> If the return type is a "keyword" known a priori like void then no
> parenthesis should be needed.


Oh, so you just special-cased some keywords and the rest require
parentheses? That's good. The only criticism I have of that is that it's
inconsistent. I had some special-casing to eliminate parentheses, similar to
what you are doing, but I ultimately sided on being consistent over
everything else. That's obviously just my opinion, so I'm not saying you
should definitely change it, it's just something to consider.

Also, given your description (using IS_UNARY), it sounds as though your
current implementation cannot handle return types that have top-level
commas. For instance, you can't return a boost::array< int, 2 > directly
without using a typedef (and if that return type is dependent on the
function's template parameters then you could potentially be stuck). I
highly recommend that you use variadic macros here to directly check if it's
anything parenthesized other than empty. It looks like you're already
relying on variadic macros for your parameter lists, so you wouldn't be
introducing further compiler requirements.



> I have done some experimentation that indicates this syntax _should_ be
> possible but it's not actually implemented yet so... we'll see :)


It's definitely possible, yes.

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
Matt Calabrese wrote
On Tue, May 31, 2011 at 5:21 PM, lcaminiti <[hidden email]> wrote:
>
> If the return type is a "keyword" known a priori like void then no
> parenthesis should be needed.


Oh, so you just special-cased some keywords and the rest require
parentheses? That's good. The only criticism I have of that is that it's
inconsistent. I had some special-casing to eliminate parentheses, similar to
what you are doing, but I ultimately sided on being consistent over
everything else. That's obviously just my opinion, so I'm not saying you
should definitely change it, it's just something to consider.
Yes, I was also concerned about inconsistency so I decided that the syntax will always accept parenthesized types. You can (but don't have to) omit the parenthesis for a known "keyword". This way users can choose as they wish.

In other words, the following will both be accepted by the same macros:

    CONTRACT_FUNCTION( (void) (f)() ) // (1)

    CONTRACT_FUNCTION( void (f)() ) // (2)

If you are worry about consistency, you use (1). If you are worried abut extra parenthesis, you use (2).

Also, given your description (using IS_UNARY), it sounds as though your
current implementation cannot handle return types that have top-level
commas. For instance, you can't return a boost::array< int, 2 > directly
without using a typedef (and if that return type is dependent on the
function's template parameters then you could potentially be stuck). I
highly recommend that you use variadic macros here to directly check if it's
anything parenthesized other than empty. It looks like you're already
Boost.Local's BOOST_IDENTITY_TYPE can be used to handle un-parenthesized commas within macro parameters (see http://svn.boost.org/svn/boost/sandbox/local/boost/utility/identity.hpp).

relying on variadic macros for your parameter lists, so you wouldn't be
introducing further compiler requirements.
Similarly to Boost.Local, the syntax will require parenthesis on preprocessors without variadics:

    CONTRACT_FUNCTION( void (f)( (int x) (int y) ) ... ) // no variadics must use this

With variadics, you can still use the above or alternatively the same macros will accept commas:

    CONTRACT_FUNCTION( void (f)( int x, int y ) ... ) // variadics can (but don't have to) use this

> I have done some experimentation that indicates this syntax _should_ be
> possible but it's not actually implemented yet so... we'll see :)

It's definitely possible, yes.
--Lorenzo
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Tue, May 31, 2011 at 5:59 PM, lcaminiti <[hidden email]> wrote:
>
> Boost.Local's BOOST_IDENTITY_TYPE can be used to handle un-parenthesized
> commas within macro parameters (see
> http://svn.boost.org/svn/boost/sandbox/local/boost/utility/identity.hpp).
>

Ah, so for a user to specify a return type of array< int, 2 >, they'd do:

(BOOST_IDENTITY_TYPE((array<int,2>)))(function_name)(params) cv-qualifiers?

That's great. You seem to be putting a lot of effort into supporting
compilers both with and without variadics.

The only problem I can think of is that the IDENTITY_TYPE kind of trick
doesn't work for function parameters where the template argument is to be
automatically deduced. For that case, I think variadics are unfortunately
necessary.

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
Matt Calabrese wrote
On Tue, May 31, 2011 at 5:59 PM, lcaminiti <[hidden email]> wrote:
>
> Boost.Local's BOOST_IDENTITY_TYPE can be used to handle un-parenthesized
> commas within macro parameters (see
> http://svn.boost.org/svn/boost/sandbox/local/boost/utility/identity.hpp).
>

Ah, so for a user to specify a return type of array< int, 2 >, they'd do:

(BOOST_IDENTITY_TYPE((array<int,2>)))(function_name)(params) cv-qualifiers?
Yes, including trailing cv-qualifiers, throw(...), etc and for my lib including precondition(...), postcondition(...).

That's great. You seem to be putting a lot of effort into supporting
compilers both with and without variadics.
Yes, I'd prefer the lib to be ISO C++ compliant.

The only problem I can think of is that the IDENTITY_TYPE kind of trick
doesn't work for function parameters where the template argument is to be
automatically deduced.
For example? (Just so I understand this case correctly.)

Thanks,
--Lorenzo
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Tue, May 31, 2011 at 6:35 PM, lcaminiti <[hidden email]> wrote:
>
> For example? (Just so I understand this case correctly.)
>

//////////
CONTRACT_FUNCTION(
public template( class T, class Alloc )
void (push_back)( (BOOST_IDENTITY_TYPE((std::vector< T, Alloc >&))) vector_,
(const T&) element ) {
  vector_.push_back(element);
}
//////////

In the above case, since the first parameter type actually ends up being the
result of a metafunction invocation, you can't simply call the function by
doing this_.push_back( an_instance_of_std_vector, 4 ). T and Alloc can no
longer be deduced.

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
Matt Calabrese wrote
On Tue, May 31, 2011 at 6:35 PM, lcaminiti <[hidden email]> wrote:
>
> For example? (Just so I understand this case correctly.)
>

//////////
CONTRACT_FUNCTION(
public template( class T, class Alloc )
void (push_back)( (BOOST_IDENTITY_TYPE((std::vector< T, Alloc >&))) vector_,
(const T&) element ) {
  vector_.push_back(element);
}
//////////

In the above case, since the first parameter type actually ends up being the
result of a metafunction invocation, you can't simply call the function by
doing this_.push_back( an_instance_of_std_vector, 4 ). T and Alloc can no
longer be deduced.
Yes, I see:

#include <boost/type_traits.hpp>
#include <memory>
#include <vector>
#include <iostream>

#define IDENTITY_TYPE(paren_type) \
    boost::function_traits< void paren_type >::arg1_type

template< typename T, class Alloc >
void push_back(
        // comma in type is now wrapped in parenthesis (ok for macros)
        typename IDENTITY_TYPE((std::vector< T, Alloc >&)) vector,
        const T& element ) {
  vector.push_back(element);
}

int main() {
    std::vector<int> v;
//    push_back(v, -1); // Error: Can't deduce vector type via IDENTITY_TYPE.
    push_back< int, std::allocator<int> >(v, -1); // OK but no type deduction :((
    std::cout << v[0] << std::endl;

    return 0;
}

Questions:
1) How bad is this? I am afraid that giving up this kind of automatic type deduction makes generic programming less convenient... am I right? ( I'm not a generic programmer ;) )
2) Does anyone know of a better way to handle commas within macro parameter types but without using variadics?

Note: Because of this I will try to provide a different implementation of IDENTITY_TYPE when variadics are supported (however, to properly handle the types with commas I think that not just IDENTITY_TYPE but also the CONTRACT macros need to modified to be aware that the type if a pp tuple). The same applies to Boost.Local macros.

Thanks a lot!
--Lorenzo
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Wed, Jun 1, 2011 at 10:01 AM, lcaminiti <[hidden email]> wrote:
>
> Questions:
> 1) How bad is this? I am afraid that giving up this kind of automatic type
> deduction makes generic programming less convenient... am I right? ( I'm
> not
> a generic programmer ;) )
>

Yes, it just makes things less convenient and that's pretty much all. It's
really not that bad since it's not an incredibly common case. Just make the
macro remove the parentheses when variadic macros are present.


> 2) Does anyone know of a better way to handle commas within macro parameter
> types but without using variadics?
>

The function parameter trick is pretty much the best (only?) way to handle
that kind of problem, at least that I'm aware of, since there's not really
anything you can do entirely during preprocessing without variadic macros.
Anyway, many or most compilers support them now (they've been standard C for
over a decade now) and, as I'm sure you know, variadic macros are in the
C++0x FDIS, so they will be standard in C++ very soon.

If I could recommend one thing, focus primarily on variadic macros and treat
the non-variadic case as a workaround, if you maintain such support at all.
Don't go overboard trying to account for users who don't have access to
compilers that can deal with variadic macros unless someone is explicitly
requesting support. The number of such users likely isn't very high at all
and it will only be getting smaller. Working with variadics simplifies
things greatly both for users and for yourself as an implementor.

Anyway, good luck. Your interface looks awesome.

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
In reply to this post by lcaminiti
lcaminiti wrote
I can eliminate a large number of extra parenthesis based on a few pp
tricks I've learned in processing Boost.Local's `const bind(type)&
var`. Plus, I can provide (optional) support for variadics macros so
to use comma-separated pp tuplets instead of parenthesized pp
sequences on preprocessors with variadics.
This is "just" a naming question. If you had a macro that returned the trait of the function access level (public, protected, private, or empty), how would you name the macro?

For example:

    PP_META_TRAITS_FUNC_ACCESS( public void (push_back)( (T const&) value ) ) // expand to `public`
    PP_META_TRAITS_FUNC_ACCESS( void (push_back)( (T const&) value ) ) // expand to nothing

I could think to name these trait macros:
1) Meta-programing: PP_META_TRAITS_...
2) Language: PP_LANG_TRAITS_...
3) Syntax: PP_SYN_TRAITS_...
4) Specification: PP_SPEC_TRAITS_...
5) Function, class, template parameters, function parameters, etc: PP_FUNC_TRAITS_... PP_CLASS_TRAITS_..., PP_TEMPLATE_PARAMS_TRAITS_, PP_FUNC_PARAMS_TRAITS_..., etc

Also (but I don't like the followings because the trait macros could also return instructions of the function body, class traits, etc so not just function signature, declaration, or definition):
6) Signature: PP_SIGN_TRAITS_...
7) Declaration: PP_DECL_TRAITS_...
8) Definition: PP_DEF_TRAITS_...

Do you have an opinion?

Thanks a lot.
--Lorenzo
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
In reply to this post by Matt Calabrese
Matt Calabrese wrote
On Tue, May 31, 2011 at 2:49 PM, Lorenzo Caminiti <[hidden email]>wrote:
>
> For example, I _think_ (but I've not implemented this yet) that I can
> simplify the syntax like this:
>

Looks good, I use pretty much exactly the same syntax in my library
concerning parameter lists and templates (
https://github.com/boostcon/2011_presentations/raw/master/thu/Boost.Generic.pdf
slides
79 and 96 are okay examples). The only thing I'm confused about is how you
Hey Matt, I am still looking over your slides so sorry in advance if the following suggestions don't make sense... Is it possible remove some of the extra parenthesis from your examples on page 79 and 96 as I suggest below? If not, why?

// Legend:
// * Parenthesis `(token)` (no spaces around single token) are to wrap a single
//   token within extra parenthesis.
// * Parenthesis `( token1 [, token2, ...] )` (spaces around token list) are to
//   specify a list of tokens.

// Page 79 //

// N2914 syntax:
concept Iterator<typename X> : Semiregular<X> {
    MoveConstructible reference = typename X::reference;
    MoveConstructible postincrement_result;
    requires HasDereference<postincrement_result>;
    reference operator*(X&);
    reference operator*(X&&);
    X& operator++(X&);
    postincrement_result operator++(X&, int);
}

// Boost.Generic syntax:
BOOST_GENERIC_CONCEPT
( (Iterator)( (typename) X ), ( public Semiregular<X> )
, ( (MoveConstructible) reference, typename X::reference )
, ( (MoveConstructible) postincrement_result )
, ( requires HasDereference<postincrement_result> )
, ( (reference)(operator dereference)( (X&) ) )
, ( (reference)(operator dereference)( (X&&) ) )
, ( (X&)(operator preincrement)( (X&) ) )
, ( (postincrement_result)(operator postincrement)( (X&), (int) ) )
)

// Is the following syntax possible?
BOOST_GENERIC(
// Use leading `concept` "keyword" to determine macro functionality.
// `typename` doesn't need extra parenthesis.
// `extends` is not necessary but I find it more readable.
concept (Iterator)( typename X ) extends( public Semiregular<X> )
    // Extra parenthesis after comma `, ( ... )` are not necessary.
    // BOOST_GENERIC_ASSIGN just expand to `,` but I find it more readable.
    , (MoveConstructible) reference BOOST_GENERIC_ASSIGN typename x::reference
    , (MoveConstructible) postincrement_result
    , requires HasDereference<postincrement_result>
    // `operator(symbol, name)` allow user to name the operators (name is any
    // arbitrary alphanumeric token) so no predefined operator name to learn.
    // Types `X&`, `X&&`, `int`, etc do not need extra parenthesis.
    , (reference) operator(*, deref)( X& )
    , (reference) operator(*, deref)( X&& )
    , (X&) operator(++, preinc)( X& )
    , (postincrement_result) operator(++, postinc)( X&, int )
)

// Page 96 //

// N2914 syntax:
template<ObjectType T> concept_map RandomAccessIterator<T*> {
    typedef T value_type;
    typedef ptrdiff_t difference_type;
    typedef T& reference;
    typedef T* pointer;
}

// Boost.Generic syntax:
BOOST_GENERIC_CONCEPT_MAP
( ( template ( (class) T ) ), (RandomAccessIterator)(T*)
, ( typedef T value_type )
, ( typedef ptrdiff_t difference_type )
, ( typedef T& reference )
, ( typedef T* pointer )
)

// Is the following syntax possible?
BOOST_GENERIC(
// Use `concept_map` "keyword" to determine macro functionality.
// Do you need ObjectType? Is was missing from above syntax...
template( (ObjectType) T ) concept_map (RandomAccessIterator)( T* )
    , typedef T value_type
    , typedef ptrdiff_t difference_type
    , typedef T& reference
    , typedef T* pointer
)

--Lorenzo
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Thu, Jun 2, 2011 at 9:28 AM, lcaminiti <[hidden email]> wrote:

> Hey Matt, I am still looking over your slides so sorry in advance if the
> following suggestions don't make sense...


First, thank you so much for taking the time to do this. I don't think
anyone other than myself has really picked apart the latest syntax and tried
to improve it.


> Is it possible remove some of the
> extra parenthesis from your examples on page 79 and 96 as I suggest below?
> If not, why?
> ...
> // Is the following syntax possible?
> BOOST_GENERIC(
> // Use leading `concept` "keyword" to determine macro functionality.


I considered this during implementation, and same with "auto" concepts, but
decided against it for a few reasons. For one, it complicates the
implementation for, imo, little or no gain, and it can impact compile-time,
which is something that I consider a problem with the library at this point
in time.


> // `typename` doesn't need extra parenthesis.
>

It does if I want to be consistent with all parameter kinds, but I could
special-case typename and class if I disallow fully qualified names as you
currently do. Right now I support typename/class, value parameters, and soon
varidiac parameters which will work via the syntax ( (ParameterKindHere,,,)
SomeNameHere ). I'll give some thought to special-casing, but for now, it's
more consistent for users and easier to preprocess if I don't do that, and
fully qualified names are handled without problem. The more I look at your
library though and the more you mention it, the more I question my choice,
so it is possible I'll start special-casing things as you are doing, but I
have a lot on my plate before I devote time to changing features that
already work.

// `extends` is not necessary but I find it more readable.
>

This was just because I wanted to use a keyword that an IDE would highlight.
In the predecessor to this library I was using words that weren't keywords
and Dave Abrahams suggested that I switch over to keywords, with a rationale
that I ultimately agreed with (easy to remember, it's not highlighted if you
make a typo). Extends is not and likely will not be a keyword in C++.

Also, with concepts, there's no such thing as "private" or "protected"
refinement -- the public there acts as "extends" acts in your example in
that it's only used to signify that what follows is a list of less refined
concepts. If I change it, the public part would just be replaced rather than
both words being there, but I'd  probably use "refines" instead of extends
since that's the proper term in Generic Programming, though that too is not
a keyword.


>    // BOOST_GENERIC_ASSIGN just expand to `,` but I find it more readable.


I'm not a big fan of that. I use , because it's short and simple, though I
have considered (,) to make it jump out as being different and unambiguous.
If you can provide a good rationale for BOOST_GENERIC_ASSIGN over those
options, I'm open to it, but at this point I favor simplicitly over a big
macro name (and my gut is that most users will too).


>    , (MoveConstructible) reference BOOST_GENERIC_ASSIGN typename


The encapsulating set of parentheses around each individual parameter are
for important reasons. For one, your current example is actually ambiguous.

//////////
(MoveConstructible) reference, typename something_here
//////////

could either be a contrained typename requirement with a default, or it
could be a constrained typename without a default followed by an
unconstrained typename. The encapsulating parentheses is imo the simplest
solution for users to remember and for implementation, although if I use
something like (,) or BOOST_GENERIC_ASSIGN for default assignment I could
disambiguate this particular issue that way.

However, another issue is that the default may, itself, have commas in it.
Without the encapsulating parentheses, I can't know if the next comma in a
default is a parameter separator or a part of the default (for instance, a
default of boost::array< int, 2 >). If I special-case it so that you have
the option of wrapping the default in parentheses if it has commas, then
it's ambiguous with the situation where there is no default and where the
next parameter to the concept macro starts with parentheses, etc. I could
keep trying to special-case further, or I could just make a single,
consistent rule that's easy to remember as I've done. IMO, this option is
simpler for both myself and users and also aids in keeping compile-times
down.

Also, since I am consistent with wrapping each parameter with parentheses,
it is much easier for me to give descriptive error reporting if the user
screws something up that I can catch at preprocessing time. For instance, if
a user is creating a many-line concept, such as an allocator-like concept,
and he makes a typo somewhere in the middle, I want to be able to give the
best error possible, including a parameter number, that particular parameter
text in full, and even be able to continue parsing the remaining parameters
to the concept, catching other potential errors during preprocessing. If
each parameter is wrapped in parentheses, it is much easier to do this --
any noticeable error that occurs in the middle of a given parameter, even if
it involves something tricky like commas, does not affect the parsing of
further parameters, and I'm able to output the full parameter text of the
parameter with the error in a static assert without accidentally cutting off
part of what the user thought was a single parameter but ends up not being
the case because of a misplaced comma.

Finally, the concept macro, as I'm sure you can imagine, is fairly nasty
underneath the hood. One nice thing about the implementation, however, is
that I handle most of the top-level implementation via a simple preprocessor
for-each operation over all of the parameters after the name and
refinements. Having the parameters consistently separated is what makes this
directly possible without any difficulty -- since the parameters are just
individual macro arguments, I use a construct that invokes a macro for each
parameter. It doesn't require any complicated logic to figure out where
individual parameters are separated, is very efficient, and it keeps the
loop logic separate from the parsing of individual parameters.


>    // `operator(symbol, name)` allow user to name the operators
>

I can't use a symbol, and a user-provided name is unfortunately meaningless
both to me and the user here. Associated operator requirements of concepts
check if the operator is callable with respect to the pseudo-signature. The
reason I use a fixed name is because, internally, I have to generate
metafunctions during preprocessing that check to see if the operation is
callable for the given parameter types, and what is generated can vary
depending on which operator is used. In order to do this, I have to know,
entirely during preprocessing, exactly which operator is specified. You
can't do this if the user provides the symbol representation of the
operator, and an arbitrary, user-specified name wouldn't help. The names I
use come from N2914 standard concept names, so there is also some
consistency here.


>    // Types `X&`, `X&&`, `int`, etc do not need extra parenthesis.


I could special case int and other built-ins, but I don't see how it would
be possible to special case X, or X&, etc. The "X" is a user-defined concept
parameter for that specific macro invocation, not a universal identifier
that I'm using.


> BOOST_GENERIC(
> // Use `concept_map` "keyword" to determine macro functionality.
>

Yes, I can do that, but again, it makes preprocessing more complicated,
affects compile-time, and is one more place for a user to make a mistake
that I would want to explicitly look for if I were to give an easy-to-read
error message. Overall, I don't think I'm gaining enough, if anything, by
pushing more stuff into the macro itself. In an ideal world, I agree, that
looks cool, but I just don't think it's practical. Simply calling the macro
BOOST_GENERIC_CONCEPT_MAP gives me all of the information that I need
without additional preprocessing.


> // Do you need ObjectType? Is was missing from above syntax...
>

Sorry, that's covered in the talk itself and is not directly in the slides
(the video is not online yet, but I'll link it whenever whoever is doing
post uploads it) -- I can't handle concept-constrained concept maps just yet
and if it's not clear, ObjectType is a concept as opposed to a type. That's
why the example is not exactly the same between the two. In the end, if you
are curious, the syntax will be

//////////
template( ((ObjectType)) T )
// or if I disallow fully-qualified names
template( (concept ObjectType) T )
//////////




   , typedef T value_type
>    , typedef ptrdiff_t difference_type
>    , typedef T& reference
>    , typedef T* pointer


No, that's not really possible in practice. The problem is that the typedefs
themselves may contain commas in them and what comes after the comma could
potentially be something not parenthesized that is not valid to concatenate
with at preprocessing time. I.E. a bool argument with a !, etc. Trying to
handle this syntax and do special casing would be complicated and
incomplete. Worse, when someone does something that looks as though it
should be valid but really is not, solely for preprocessor reasons that I
can't explicitly check for such as the one mentioned above, they would get a
strange preprocessor concatenation error that would likely be completely
meaningless to them. I try hard to make it so that the user should not be
seeing errors like that, and without them having to remember what may seem
like arbitrary rules that only make sense if you have written something
along the lines of this library or your library, or Boost.Parameter, etc.
which most programmers have not. Even then, the issues are extremely subtle.

Again, thanks for all of this feedback. I've been wanting another head to
kind of tear things apart. If anything, I think I may go ahead and
special-case parameters like void, class, typename, etc. though that won't
be for a while. Not using a comma as a replacement for = when specifying
defaults may happen too. All of the other special-casing and attempted
removal of parentheses I'll have to give further thought to, but I think the
current design has the least subtle gotchas.

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Thu, Jun 2, 2011 at 1:56 PM, Matt Calabrese <[hidden email]> wrote:

> Not using a comma as a replacement for = when specifying defaults may
> happen too.
>

Actually, I just realized that (,) is not even an option, so there goes
that. Unless you can come up with something other than , that
BOOST_GENERIC_ASSIGN would expand out to, I think I'm going to stick with
comma.

Maybe two commas :/

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
In reply to this post by Matt Calabrese
On Thu, Jun 2, 2011 at 1:56 PM, Matt Calabrese <[hidden email]> wrote:
> On Thu, Jun 2, 2011 at 9:28 AM, lcaminiti <[hidden email]> wrote:
>
>> Hey Matt, I am still looking over your slides so sorry in advance if the
>> following suggestions don't make sense...
>
>
> First, thank you so much for taking the time to do this. I don't think
> anyone other than myself has really picked apart the latest syntax and tried
> to improve it.

:)

>> Is it possible remove some of the
>> extra parenthesis from your examples on page 79 and 96 as I suggest below?
>> If not, why?
>> ...
>> // Is the following syntax possible?
>> BOOST_GENERIC(
>> // Use leading `concept` "keyword" to determine macro functionality.
>
>
> I considered this during implementation, and same with "auto" concepts, but
> decided against it for a few reasons. For one, it complicates the
> implementation for, imo, little or no gain, and it can impact compile-time,
> which is something that I consider a problem with the library at this point
> in time.

OK.

>> // `typename` doesn't need extra parenthesis.
>>
>
> It does if I want to be consistent with all parameter kinds, but I could
> special-case typename and class if I disallow fully qualified names as you
> currently do. Right now I support typename/class, value parameters, and soon
> varidiac parameters which will work via the syntax ( (ParameterKindHere,,,)
> SomeNameHere ). I'll give some thought to special-casing, but for now, it's
> more consistent for users and easier to preprocess if I don't do that, and
> fully qualified names are handled without problem. The more I look at your
> library though and the more you mention it, the more I question my choice,
> so it is possible I'll start special-casing things as you are doing, but I
> have a lot on my plate before I devote time to changing features that
> already work.

My new syntax also supports these cases:

    template< class T = int, typename U, T x, int y = 0 > // usual C++

    template( class T, default int, typename U, (T) x, int y, default
0 ) // contract syntax

If for consistency you want to use extra parenthesis around all types,
the following also works:

    template( class T, default int, typename U, (T) x, (int) y, default 0 )

> // `extends` is not necessary but I find it more readable.
>>
>
> This was just because I wanted to use a keyword that an IDE would highlight.
> In the predecessor to this library I was using words that weren't keywords
> and Dave Abrahams suggested that I switch over to keywords, with a rationale
> that I ultimately agreed with (easy to remember, it's not highlighted if you
> make a typo). Extends is not and likely will not be a keyword in C++.
>
> Also, with concepts, there's no such thing as "private" or "protected"
> refinement -- the public there acts as "extends" acts in your example in
> that it's only used to signify that what follows is a list of less refined
> concepts. If I change it, the public part would just be replaced rather than
> both words being there, but I'd  probably use "refines" instead of extends
> since that's the proper term in Generic Programming, though that too is not
> a keyword.

This still keeps public that is highlighted (BTW, in the library docs
I will provide the DSEL grammar and a list of the new "keyword" like
extends, requires, precondition, etc so you can configure your IDE to
color them up) and yes, refines makes more sense:

    refines( public Semiregular<T> )

>>    // BOOST_GENERIC_ASSIGN just expand to `,` but I find it more readable.
>
>
> I'm not a big fan of that. I use , because it's short and simple, though I
> have considered (,) to make it jump out as being different and unambiguous.
> If you can provide a good rationale for BOOST_GENERIC_ASSIGN over those
> options, I'm open to it, but at this point I favor simplicitly over a big
> macro name (and my gut is that most users will too).
>
>
>>    , (MoveConstructible) reference BOOST_GENERIC_ASSIGN typename
>
>
> The encapsulating set of parentheses around each individual parameter are
> for important reasons. For one, your current example is actually ambiguous.
>
> //////////
> (MoveConstructible) reference, typename something_here
> //////////
>
> could either be a contrained typename requirement with a default, or it
> could be a constrained typename without a default followed by an
> unconstrained typename. The encapsulating parentheses is imo the simplest
> solution for users to remember and for implementation, although if I use
> something like (,) or BOOST_GENERIC_ASSIGN for default assignment I could
> disambiguate this particular issue that way.

I used ", default ..." for default parameters:

    (MoveConstructible) reference, default typename x::reference

> However, another issue is that the default may, itself, have commas in it.
> Without the encapsulating parentheses, I can't know if the next comma in a
> default is a parameter separator or a part of the default (for instance, a
> default of boost::array< int, 2 >). If I special-case it so that you have
> the option of wrapping the default in parentheses if it has commas, then
> it's ambiguous with the situation where there is no default and where the
> next parameter to the concept macro starts with parentheses, etc. I could
> keep trying to special-case further, or I could just make a single,
> consistent rule that's easy to remember as I've done. IMO, this option is
> simpler for both myself and users and also aids in keeping compile-times
> down.

To handle unparenthesized commas, I'd use the IDENTITY_TYPE/VALUE
macros. Commas not wrapped within parenthesis are rare so IMO it's not
worth to ask users to always use extra parenthesis to handle them.
It's simpler to ask user to use a special macro when they are present.

> Also, since I am consistent with wrapping each parameter with parentheses,

I also went down this path and called the previous syntax
"parenthesized syntax" because all tokens were consistently wrapped
within extra parenthesis. Then I was think it's easier for users to
learn the syntax "just wrap all tokens of the usual C++ syntax within
parenthesis (more or less)". Everyone that looked at the syntax
complained about the extra parenthesis :(( I also now think that the
newer syntax with only a few extra parenthesis is better.

> it is much easier for me to give descriptive error reporting if the user
> screws something up that I can catch at preprocessing time. For instance, if
> a user is creating a many-line concept, such as an allocator-like concept,
> and he makes a typo somewhere in the middle, I want to be able to give the
> best error possible, including a parameter number, that particular parameter
> text in full, and even be able to continue parsing the remaining parameters
> to the concept, catching other potential errors during preprocessing. If
> each parameter is wrapped in parentheses, it is much easier to do this --
> any noticeable error that occurs in the middle of a given parameter, even if
> it involves something tricky like commas, does not affect the parsing of
> further parameters, and I'm able to output the full parameter text of the
> parameter with the error in a static assert without accidentally cutting off
> part of what the user thought was a single parameter but ends up not being
> the case because of a misplaced comma.

Error reporting is a good point especially because all your code is
within the macros (for Boost.Local the function body code is outside
the macro and that's were most of the actual programming errors will
be so I likely don't have to worry about error reporting too much -- I
still check the syntax using the pp as much as possible).

> Finally, the concept macro, as I'm sure you can imagine, is fairly nasty
> underneath the hood. One nice thing about the implementation, however, is
> that I handle most of the top-level implementation via a simple preprocessor
> for-each operation over all of the parameters after the name and
> refinements. Having the parameters consistently separated is what makes this
> directly possible without any difficulty -- since the parameters are just
> individual macro arguments, I use a construct that invokes a macro for each
> parameter. It doesn't require any complicated logic to figure out where
> individual parameters are separated, is very efficient, and it keeps the
> loop logic separate from the parsing of individual parameters.
>
>
>>    // `operator(symbol, name)` allow user to name the operators
>>
>
> I can't use a symbol, and a user-provided name is unfortunately meaningless
> both to me and the user here. Associated operator requirements of concepts
> check if the operator is callable with respect to the pseudo-signature. The
> reason I use a fixed name is because, internally, I have to generate
> metafunctions during preprocessing that check to see if the operation is
> callable for the given parameter types, and what is generated can vary
> depending on which operator is used. In order to do this, I have to know,
> entirely during preprocessing, exactly which operator is specified. You
> can't do this if the user provides the symbol representation of the
> operator, and an arbitrary, user-specified name wouldn't help. The names I
> use come from N2914 standard concept names, so there is also some
> consistency here.

I see.

>>    // Types `X&`, `X&&`, `int`, etc do not need extra parenthesis.
>
>
> I could special case int and other built-ins, but I don't see how it would
> be possible to special case X, or X&, etc. The "X" is a user-defined concept
> parameter for that specific macro invocation, not a universal identifier
> that I'm using.

    , (postincrement_result) operator(++, postinc)( X&, int )

That's a tuple and X& is the only token... why can't you just get it with:

    PP_VARIADIC_TUPLE_ELEM(0, (X&, int)) // expand to X&

I am missing something here... maybe sometimes you have some other
tokens like in ( (X&) x, int y ) ??

>> BOOST_GENERIC(
>> // Use `concept_map` "keyword" to determine macro functionality.
>>
>
> Yes, I can do that, but again, it makes preprocessing more complicated,
> affects compile-time, and is one more place for a user to make a mistake
> that I would want to explicitly look for if I were to give an easy-to-read
> error message. Overall, I don't think I'm gaining enough, if anything, by
> pushing more stuff into the macro itself. In an ideal world, I agree, that
> looks cool, but I just don't think it's practical. Simply calling the macro
> BOOST_GENERIC_CONCEPT_MAP gives me all of the information that I need
> without additional preprocessing.
>
>
>> // Do you need ObjectType? Is was missing from above syntax...
>>
>
> Sorry, that's covered in the talk itself and is not directly in the slides
> (the video is not online yet, but I'll link it whenever whoever is doing
> post uploads it) -- I can't handle concept-constrained concept maps just yet
> and if it's not clear, ObjectType is a concept as opposed to a type. That's
> why the example is not exactly the same between the two. In the end, if you
> are curious, the syntax will be
>
> //////////
> template( ((ObjectType)) T )
> // or if I disallow fully-qualified names
> template( (concept ObjectType) T )
> //////////
>
>
>
>
>   , typedef T value_type
>>    , typedef ptrdiff_t difference_type
>>    , typedef T& reference
>>    , typedef T* pointer
>
>
> No, that's not really possible in practice. The problem is that the typedefs
> themselves may contain commas in them and what comes after the comma could
> potentially be something not parenthesized that is not valid to concatenate
> with at preprocessing time. I.E. a bool argument with a !, etc. Trying to

Again, can't you use IDENTITY_TYPE to handle unwrapped commas as
special cases? That way you simply the syntax removing the extra
parenthesis for the common cases (unwrapped commas are rare in my
experience).

> handle this syntax and do special casing would be complicated and
> incomplete. Worse, when someone does something that looks as though it
> should be valid but really is not, solely for preprocessor reasons that I
> can't explicitly check for such as the one mentioned above, they would get a
> strange preprocessor concatenation error that would likely be completely
> meaningless to them. I try hard to make it so that the user should not be
> seeing errors like that, and without them having to remember what may seem
> like arbitrary rules that only make sense if you have written something
> along the lines of this library or your library, or Boost.Parameter, etc.
> which most programmers have not. Even then, the issues are extremely subtle.
>
> Again, thanks for all of this feedback. I've been wanting another head to
> kind of tear things apart. If anything, I think I may go ahead and
> special-case parameters like void, class, typename, etc. though that won't
> be for a while. Not using a comma as a replacement for = when specifying
> defaults may happen too. All of the other special-casing and attempted
> removal of parentheses I'll have to give further thought to, but I think the
> current design has the least subtle gotchas.

Thanks to you to take a look at Boost.Contract newer syntax. BTW, this
is major syntax re-design #4 for Boost.Contract... it requires a lot
of patience and perseverance :)

--Lorenzo
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Matt Calabrese
On Sat, Jun 4, 2011 at 3:37 PM, Lorenzo Caminiti <[hidden email]>wrote:

> This still keeps public that is highlighted (BTW, in the library docs
> I will provide the DSEL grammar and a list of the new "keyword" like
> extends, requires, precondition, etc so you can configure your IDE to
> color them up)


Yeah, I set my IDE to highlight requires as well, as you can see in the
slides, but the thing I don't really like about doing it for "extends", is
that it's likely not going to be a keyword even in the standard after 0x.
So, if someone uses "extends" in their code in a place completely separate
from the macro, I think it's not a great idea to have it highlight,
otherwise it makes it seem like a user should not be using "extends" as an
identifier when in actuality it's perfectly fine. For words like requires,
however, since it's very likely that in the next standard it will be a
keyword anyway, it's probably a good thing that people see it highlighted
now since using it as an identifier will probably break sometime in the next
decade (perhaps I'm being optimistic :p).


> I used ", default ..." for default parameters:
>
>    (MoveConstructible) reference, default typename x::reference
>

Yeah, I really like your use of default there! I'm going to give it some
thought, and I probably will go that route too.

To handle unparenthesized commas, I'd use the IDENTITY_TYPE/VALUE
> macros. Commas not wrapped within parenthesis are rare so IMO it's not
> worth to ask users to always use extra parenthesis to handle them.
> It's simpler to ask user to use a special macro when they are present.
>

I'm not too sure that I agree with the statement that top-level commas are
rare. Templates with multiple parameters aren't that uncommon in the real
world, are they? Or am I really out-of-touch with the "real world" here (I
admit, this is a possibility, but at least of those who would use a macro
library like this, I think it's not that odd)? Either way, if it really is
rare, I think I still would rather have the user not have to do anything
different when they eventually do write a typedef that contains commas. I
like having the corner-cases work with the same syntax as the common case.

Error reporting is a good point especially because all your code is
> within the macros (for Boost.Local the function body code is outside
> the macro and that's were most of the actual programming errors will
> be so I likely don't have to worry about error reporting too much -- I
> still check the syntax using the pp as much as possible).
>

Yeah, error reporting at preprocessor time ends up being a huge hassle for
me, though I've got it down to a system that's not too difficult to manage
now. I've even started checking during preprocessing that associated
operators have the correct amount of parameters so that users don't get
compile-errors in generated code.

   , (postincrement_result) operator(++, postinc)( X&, int )
>
> That's a tuple and X& is the only token... why can't you just get it with:
>
>    PP_VARIADIC_TUPLE_ELEM(0, (X&, int)) // expand to X&
>

> I am missing something here... maybe sometimes you have some other
> tokens like in ( (X&) x, int y ) ??
>

Yup, that's exactly it. The issue is that the user can optionally provide a
name for each individual parameter (this is true for all of my parameter
lists). So while yes, if a user doesn't provide a name, I can pull out the
individual parameter type, but if a name is provided then I can't separate
the parameter name from the parameter type, which I need to do underneath
the hood. Without the parameter being parenthesized or it being a known name
such as int, I can't determine if a parameter name is even there, let alone
separate that name from the type.


> Thanks to you to take a look at Boost.Contract newer syntax. BTW, this
> is major syntax re-design #4 for Boost.Contract... it requires a lot
> of patience and perseverance :)
>

Yeah, definitely understood! Awesome work.

--
-Matt Calabrese
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
In reply to this post by lcaminiti
lcaminiti wrote
For example, I _think_ (but I've not implemented this yet) that I can
simplify the syntax like this:

#include <contract.hpp>
#include <vector>
#include "myvector/pushable.hpp"

// Wrapper class that adds contracts to std::vector.
CONTRACT_CLASS(
template( typename T )
class (myvector) extends( public pushable<T> ) // Subcontracting.
) {
    CONTRACT_CLASS_INVARIANT(
        empty() == (size() == 0)
        // More invariants here (comma separated)...
    )

    CONTRACT_FUNCTION(
    public void (push_back)( (const T&) element )
        precondition(
            size() < max_size()
            // More preconditions here (comma separated)...
        )
        postcondition(
            auto old_size = CONTRACT_OLDOF(size()), // Old value.
            size() == old_size + 1
            // More postconditions here (comma separated)...
        )
    ) {
        vector_.push_back(element); // Implementation.
    }

    // Rest of the class here (with more contracts if needed)...
public:
    typedef typename std::vector<T>::size_type size_type;
    size_type size(void) const { return vector_.size(); }
    size_type max_size(void) const { return vector_.max_size(); }
    bool empty(void) const { return vector_.empty(); }
    const T& back(void) const { return vector_.back(); }
private:
    std::vector<T> vector_;
};

For a side-by-side comparison with N1962 syntax:
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html/index.html#contract__.introduction.an_example
For a comparison of the entire std::vector (plus Boost.ConceptCheck concepts):
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html/contract__/examples.html
For a comparison for all Boost.Contract features (plus
Boost.ConceptCheck concepts and Boost.Parameter named parameters):
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html/contract__/tutorial.html#contract__.tutorial.a_fully_working_example
(Don't pay attention to the rest of the docs that are way out of date.)
I was able to implement this syntax. If anyone is curious, I have attached the complete grammar of the syntax and the macro APIs to inspect the syntax's meta-traits:

grammar03.cpp

For example, the macro APIs allow to program the following:

#include <contract/detail/preprocessor/traits/func.hpp>
#include <boost/preprocessor/facilities/is_empty.hpp>
#include <boost/preprocessor/logical/compl.hpp>

#define TRAITS_add CONTRACT_DETAIL_PP_FUNC_TRAITS( \
    int (add) ( int x, int y ) \
)

#define TRAITS_push_back CONTRACT_DETAIL_PP_FUNC_TRAITS( \
    public virtual void (push_back) ( (T&) value ) \
)

#define IS_VIRTUAL(func_traits) \
    BOOST_PP_COMPL(BOOST_PP_IS_EMPTY(CONTRACT_DETAIL_PP_FUNC_TRAITS_VIRTUAL( \
            func_traits)))

IS_VIRTUAL(TRAITS_add) // expand to 0 (not declared virtual)
IS_VIRTUAL(TRAITS_push_back) // expand to (declared virtual)

#undef TRAITS_add
#undef TRAITS_push_back

Traits macros source code at:
http://contractpp.svn.sourceforge.net/viewvc/contractpp/branches/syn/src/contract/detail/preprocessor/traits/

--Lorenzo
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

lcaminiti
In reply to this post by lcaminiti
On Tue, May 31, 2011 at 2:49 PM, Lorenzo Caminiti <[hidden email]> wrote:

> I can eliminate a large number of extra parenthesis based on a few pp
> tricks I've learned in processing Boost.Local's `const bind(type)&
> var`. Plus, I can provide (optional) support for variadics macros so
> to use comma-separated pp tuplets instead of parenthesized pp
> sequences on preprocessors with variadics.
>
> For example, I _think_ (but I've not implemented this yet) that I can
> simplify the syntax like this:
>
> #include <contract.hpp>
> #include <vector>
> #include "myvector/pushable.hpp"
>
> // Wrapper class that adds contracts to std::vector.
> CONTRACT_CLASS(
> template( typename T )
> class (myvector) extends( public pushable<T> ) // Subcontracting.
> ) {
>    CONTRACT_CLASS_INVARIANT(
>        empty() == (size() == 0)
>        // More invariants here (comma separated)...
>    )
>
>    CONTRACT_FUNCTION(
>    public void (push_back)( (const T&) element )
>        precondition(
>            size() < max_size()
>            // More preconditions here (comma separated)...
>        )
>        postcondition(
>            auto old_size = CONTRACT_OLDOF(size()), // Old value.
>            size() == old_size + 1
>            // More postconditions here (comma separated)...
>        )
>    ) {
>        vector_.push_back(element); // Implementation.
>    }
>
>    // Rest of the class here (with more contracts if needed)...
> public:
>    typedef typename std::vector<T>::size_type size_type;
>    size_type size(void) const { return vector_.size(); }
>    size_type max_size(void) const { return vector_.max_size(); }
>    bool empty(void) const { return vector_.empty(); }
>    const T& back(void) const { return vector_.back(); }
> private:
>    std::vector<T> vector_;
> };

For whomever is curious, I have implemented the new Boost.Contract
syntax. Here's large number of examples that now compile with the new
syntax:
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html

Highlights are:
* Side-by-side comparison with N1962 (contracts) and N2081 (concepts) syntax:
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html#contract__.examples.n1962_vector
* I have also added C++0x-like virtual specifiers (final, override,
and new) to Boost.Contract:
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html#contract__.examples.mitchell02_counter
* Side-by-side comparison with Eiffel syntax:
    http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html#contract__.examples.meyer97_stack4

Comments are always welcome :)

(I still have to implement named parameter support (using
Boost.Parameter behind the scene) but that shouldn't be too hard. The
macros already parse the named parameter syntax using in/out/inout
"keywords".)

--Lorenzo

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Dave Abrahams

on Fri Sep 02 2011, Lorenzo Caminiti <lorcaminiti-AT-gmail.com> wrote:

> For whomever is curious, I have implemented the new Boost.Contract
> syntax. Here's large number of examples that now compile with the new
> syntax:
>     http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html

Wow.  This is a powerful new language!  How does this relate to the work
Matt Calabrese started on implementing concepts in C++11 (see the
boostcon video)?

It looks like this can't possibly be quite right (can it?)

http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html#contract__.examples.__n2081___find__generic_find_algorithm

Don't you need a comma after "boost::InputIterator<Iter>"?

> Highlights are:
> * Side-by-side comparison with N1962 (contracts) and N2081 (concepts) syntax:
>     http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html#contract__.examples.n1962_vector* I have also added C++0x-like virtual specifiers (final, override,
> and new) to Boost.Contract:
>     http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html#contract__.examples.mitchell02_counter* Side-by-side comparison with Eiffel syntax:
>     http://svn.boost.org/svn/boost/sandbox/contract/libs/contract/doc/html2/contract__/examples.html#contract__.examples.meyer97_stack4
>
> Comments are always welcome :)

* Very impressive!  I might even try programming in this language.
* How's debuggability (sorry for asking)?
* What's a "loop variant?"  I've heard of "loop invariants."

> (I still have to implement named parameter support (using
> Boost.Parameter behind the scene) but that shouldn't be too hard. The
> macros already parse the named parameter syntax using in/out/inout
> "keywords".)

I can't wait.

--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: [contract] syntax redesign

Andrzej Krzemienski
In reply to this post by lcaminiti
> 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

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;
        }
    }

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;
    }


Regards,
&rzej

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
123