Boost logo

Boost :

Subject: Re: [boost] Tick: Trait instrospection library for C++14
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2014-05-31 16:03:43


Le 31/05/14 18:35, pfultz2 a écrit :
>> What is the added value of your library respect to Eric work?
> Its actually based on Eric Niebler's framework. Now there is a lot of
> details
> about it that I don't know, since it is mostly undocumeted. In his
> framework, an
> `Incrementable` concept would be built like this:
>
> struct Incrementable
> {
> template<class T>
> auto requires(T&& x) -> decltype(concepts::valid_expr(
> (x++, 42),
> ++x
> ));
> };
>
> template<class T>
> using is_incrementable = concept::models<Incrementable, T>;
>
> The whole `42` is needed to avoid problems if `x++` returns `void`. In Tick,
> `is_incrementable` can be defined in a similiar manner:
>
> struct Incrementable : tick::ops
> {
> template<class T>
> auto requires(T&& x) -> TICK_VALID(
> x++,
> ++x
> );
> };
>
> template<class T>
> using is_incrementable = tick::trait<Incrementable(T)>;

Ok, I can see the advantage of using macros, but please not too much.

>
> Now, the biggest difference is the `TICK_VALID` macro which works around the
> returning `void` problem, so I don't need to use the `42` hack. The second
> difference is that I call `tick::trait` rather than `concept::models`, but
> they
> work in a similiar manner(perhaps it would make send to rename it to
> `models`).
I like concept::models. tick_traits doesn't mean nothing significant.
Why do you need tick:ops?
>
> Now I feel there is a decent amount of boilerplate using ` tick::trait`(or
> `concept::models`), so the `TICK_TRAIT` macro just simplifies that process.
I agree that there is some boilerplate.

First Eric defines the concept constraints in a separated
detail::Incrementable struct.
Then then defines an alias Incrementable to
concept::models<detail::Incrementable,T> that check the constraints.
This alias is used inside the CONCEPT_REQUIRES

                 CONCEPT_REQUIRES_(
                     Incrementable<T>() &&
                     Decrementable<T>()
                 )

I can live with that.
I have the impression that the the underlying classes behind the macros
are more complex as it seems that they have been adapted so that tht
macros can generate the code.
>
> Next, in Eric Niebles's framework as well in Tick, refinements to traits can
> be
> defined. So if we want `is_incrementable` to also be copyable, we need to
> define
> a `CopyConstructible` concept and then refine it, something like this(I
> could be
> wrong about some of these details):
>
> struct CopyConstructible
> {
> template<class T>
> auto requires(T&& x) -> decltype(concepts::valid_expr(
> concepts::is_true(std::is_copy_constuctible<T>{})
> ));
> };
>
> struct Incrementable
> : refines<CopyConstructible>
> {
> template<class T>
> auto requires(T&& x) -> decltype(concepts::valid_expr(
> (x++, 42),
> ++x
> ));
> };
>
> template<class T>
> using is_incrementable = concept::models<Incrementable, T>;
>
> In Tick, we use placeholder expressions to define refinements from other
> traits.
> We also aren't restricted to just concepts, so we can reuse other
> traits(such as
> `std::is_copy_constructible`), so in Tick we would just simply define:
>
> struct Incrementable
> : tick::refines<std::is_copy_constructible&lt;tick::_>>, tick::ops
> {
> template<class T>
> auto requires(T&& x) -> TICK_VALID(
> x++,
> ++x
> );
> };
>
> template<class T>
> using is_incrementable = tick::trait<Incrementable(T)>;
>
> Also, refinements can be added to the macro version as well:
>
> TICK_TRAIT(is_incrementable, std::is_copy_constructible<_>)
> {
> template<class T>
> auto requires(T&& x) -> TICK_VALID(
> x++,
> ++x
> );
> };
when I compare to what I need

     struct Incrementable
     : refines<CopyConstructible>
     {
         template<class T>
         auto requires(T&& x) -> decltype(concepts::valid_expr(
             (x++, 42),
             ++x
         ));
     };

I really prefer the last one (even if the 42is magic). I need to admit
that the macro TICK_VALID could be useful.
BTW, how do you know how many template parameters has the concept?
Could a concept in your library have a non-type template parameter?

> Notice we don't need the `tick::` namespace for the placeholders anymore.
No need for placeholders in Eric version.
> Also,
> tick provides `TICK_TRAIT_CHECK` which will output to the compiler all that
> traits that failed including any traits that were refined(ie base traits).
>
> Also, types can be matched against other traits using placeholder
> expressions,
> so if we want `some_trait` that checks if `x.foo()` is a fusion sequence, we
> would just do this:
>
> TICK_TRAIT(some_trait)
> {
> template<class T>
> auto requires(T&& x) -> TICK_VALID(
> returns<boost::fusion::is_sequence&lt;_>>(x.foo())
> );
> };
>
> Something similiar can be done in Eric's framework but it would require
> wrapping
> `boost::fusion::is_sequence` in a concept, something like this:
>
> struct FusionSequence
> {
> template<class T>
> auto requires(T&& x) -> decltype(concepts::valid_expr(
> concepts::is_true(boost::fusion::is_sequence<T>{})
> ));
> };
>
> struct SomeConcept
> {
> template<class T>
> auto requires(T&& x) -> decltype(concepts::valid_expr(
> concept::model_of<FusionSequence>(x.foo())
> ));
> };
>
> template<class T>
> using some_trait = tick::trait<SomeConcept(T)>;
This FusionSequence concept is not completely necessary even if it could
be clearer. We need just

     struct SomeConcept
     {
         template<class T>
         auto requires(T&& x) -> decltype(concepts::valid_expr(
             concepts::is_true(boost::fusion::is_sequence<T>{})
         ));
     };

> Finally, I don't support adding nested types, like in the example you gave
> with
Why?
> `Addable::result_t`:
>
> struct Addable
> {
> template<typename T, typename U>
> using result_t = decltype(std::declval<T>() + std::declval<U>());
>
> template<typename T>
> auto requires(T && t) -> decltype(concepts::valid_expr(
> t + t
> ));
>
> template<typename T, typename U>
> auto requires(T && t, U && u) -> decltype(concepts::valid_expr(
> t + u
> ));
> };
>
> In practice, I find it better to write a separate trait(or metafunction) for
> this. It provides better reusability and composability. It seems the C++
> community has already progressed in this direction as we have moved from
> using
> `iterator_traits<T>::reference` to using `iterator_reference<T>::type`. I
> could
> add the ability to make this usable in Tick if there was a real need for it.
>
>
>
How would you then write the Addable concept?

I don't think that we need to add limitations, but expressiveness. BTW,
what is the intent of

         template<typename T, typename U>
         using result_t = decltype(std::declval<T>() + std::declval<U>());

I prefer the concept::models<Concept, Model> expression to the trait
expression tick::trait<isConcept(Model)>.

I suggest you to provide a non-macro library that will be at least as
expressive as Eric's one and only on top of this expressive library add
some macros that make the user life easier.

What about working with Eric? I'm sure he would be happy to don't
maintain the concepts part of range-v3.

Anyway I would like to see something like this very soon on Boost, so if
you don't find a better review manager you could contact me.

Best,
Vicente


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