Boost logo

Boost :

Subject: Re: [boost] Tick: Trait instrospection library for C++14
From: pfultz2 (pfultz2_at_[hidden])
Date: 2014-05-31 23:23:57


> If the concept is defined in the namespace concepts this is not needed
> as all these is also in the concepts namespace.
> Maybe the user should not define its concepts in the same namespace.

As a general purpose library, traits and concepts will need to be defined in
different namespaces. For scalability reasons they all can't be defined in
the
same namespace.

> I believe you "sur parolle".
> What about needing to use placeholders when stating refinement?
> Why do you need
>
> TICK_TRAIT(is_x, is_Y<_>)
>
> instead of
>
> TICK_TRAIT(is_X, is_Y)

First, placeholder expressions are more flexible than template template
parameters. With placeholder expressions I can support non-type template
parameters(I don't support them yet), which cannot be done with template
template parameters. Secondly, placeholder expressions are more powerful, so
say I want to write `is_equality_comparable` trait and have each parameter
refine `std::is_default_constructible`, like this:

    struct EqualityComparable
    : tick::refines<std::is_default_constructible&lt;tick::_1>,
std::is_default_constructible<tick::_2>>
    {
        template<class T, class U>
        auto requires(T&& x, U&& y) -> TICK_VALID(
            x == y,
            x != y
        );
    };

    template<class T, class U>
    using is_equality_comparable = tick::trait<EqualityComparable(T, U)>;

I believe Eric's framework actually supports placeholders in a similiar way.
I
believe you can write something like this(assuming we have a
`DefaulConstructible` concept already defined):

    struct EqualityComparable
    : concept::refines<DefaultConstructible(_1), DefaultConstructible(_2)>
    {
        template<class T, class U>
        auto requires(T&& x, U&& y) -> decltype(concept::valid_expr(
            x == y,
            x != y
        ));
    };

    template<class T, class U>
    using is_equality_comparable = concept::models<EqualityComparable, T,
U>;

Since its not documented, I'm not sure if this entirely correct.

Now the same can be done in Tick using the macro based version as well:

    TICK_TRAIT(is_equality_comparable,
        std::is_default_constructible<_1>,
        std::is_default_constructible<_2>)
    {
        template<class T, class U>
        auto requires(T&& x, U&& y) -> TICK_VALID(
            x == y,
            x != y
        );
    };

The only difference is the placeholders are local. The macro does this by
defining a private class, and bringing the placeholders(from `tick::ops`),
so
something like this:

    struct private_base_trait_is_equality_comparable
    : tick::ops
    {
        typedef tick::refines<std::is_default_constructible&lt;_1>,
std::is_default_constructible<_2>> type;
    };

    struct private_trait_is_equality_comparable
    : tick::ops, private_base_trait_is_equality_comparable::type
    {
        template<class T, class U>
        auto requires(T&& x, U&& y) -> TICK_VALID(
            x == y,
            x != y
        );
    };

    template<class... Ts>
    using is_equality_comparable =
tick::trait<private_trait_is_equality_comparable(Ts...)>;

This is all just implementation details.

> I don't see the risk to be a valid reason to don't make it public and
> documenting it. Let the user the choice to don't use macros please.

I would prefer not to give the user something that is broken, and there is
no
disadvantage to using the macro.

> Yes, but the macro needs to generate the is_trait class. Ah, I see it on
> the code now, you have genrated a variadic template for any is_trait.
>
> template<class... T> \
> struct name \
>
> I would prefer that the concept template had the good template members.
> Could this be done?
>
> |TICK_TRAIT(is_incrementable, T, refines,|||is_integral<_>|)|

This would get complicated real fast, Especially if the user wanted to
define
multiple overloads. Also, its starting to look real ugly. If the user wants
that kind of control, I think its best not to use the macros.

> >> Could a concept in your library have a non-type template parameter?
> > It could, but behind scenes it would require wrapping it in a type.
> Athough,
> > I
> > could work on adding better support for non-type template parameters.
> Isn't this a consequence of using macros?

No it isn't. It is due to the fact that the `tick::trait`(or `models` if you
prefer) only takes type parameters. So the trait can take a non-type
parameter, but it would need to be wrapped in a type to pass to
`tick::trait`
something like this:

    struct Zeroed : tick::ops
    {
        template<class Int>
        auto requires(Int) -> TICK_VALID(
            is_true<bool_&lt;Int() == 0>>()
        );
    }

    template<int N>
    using is_zero = tick::trait<Zeroed(int_&lt;N>)>;

Eric's framework would need to do a similiar thing as well.

> This depends where your concept is defined. If it is defined in concepts
> this works ;-)
> You have the same namespace problem if the traits are defined in
> different namespapces.

No, not exaclty. The problem is the user has two different classes to refer
to
the essentially same thing(ie `Incrementable` and
`concepts::Incrementable`).
Sometimes the user must refer to it as `Incrementable` and sometimes
`concepts::Incrementable`. For simplicity sake, Tick treats the concept as
an
implementation detail and refers to everything by the trait. The fact that
the
user may confuse `boost::mpl::is_sequence` and `boost::fusion::is_sequence`
is
a different problem entirely.

> Please could you explain what
>
> returns<boost::fusion::is_sequence&lt;_>>(x.foo())
>
>
> checks? That there is a function member foo and ...

And it returns a fusion sequence, that is `x.foo()` returns something that
matches the `boost::fusion::is_sequence` trait(ie
`boost::fusion::is_sequence`
trait is true for what `x.foo()` returns).

> This check the function member foo returns a model of a Fusion sequence,
> isn't it?

Yes thats true.

> >>> Finally, I don't support adding nested types, like in the example you
> >> gave
> >>> with
> >> Why?
> > Its better to write it as a seperate trait, like this:
> >
> > template<typename T, typename U>
> > using addable_result =
> boost::identity<decltype(std::declval&amp;lt;T>() +
> > std::declval<U>())>;
> >
> > This allows for better reusability and composability.
>
> All good reasons, but as always is a taste question. Is there something
> that prevents using them?

Only that the library treats that class as an implementation detail, so it
is
not always easily accessed. However, in your own library you could make this
class available by not using the `TICK_TRAIT` macro.

--
View this message in context: http://boost.2283326.n4.nabble.com/Tick-Trait-instrospection-library-for-C-14-tp4662744p4663178.html
Sent from the Boost - Dev mailing list archive at Nabble.com.

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