Boost logo

Boost :

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


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

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`).

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.

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

Notice we don't need the `tick::` namespace for the placeholders anymore.
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)>;

Finally, I don't support adding nested types, like in the example you gave
with
`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.

--
View this message in context: http://boost.2283326.n4.nabble.com/Tick-Trait-instrospection-library-for-C-14-tp4662744p4663150.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