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
; 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
;
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::modelsdetail::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
; 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::refinesstd::is_copy_constructible<tick::_>, tick::ops { template<class T> auto requires(T&& x) -> TICK_VALID( x++, ++x ); };
template<class T> using is_incrementable = tick::trait
; 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( returnsboost::fusion::is_sequence<_>(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
; 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
using result_t = decltype(std::declval<T>() + std::declval<U>()); template<typename T> auto requires(T && t) -> decltype(concepts::valid_expr( t + t ));
template
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