Tick: Trait instrospection library for C++14
Hi, I developed a trait introspection library for C++14 based on Eric Niebler's concept checking, here: https://github.com/pfultz2/Tick So you can build a simple `is_incrementable` trait like this: TICK_TRAIT(is_incrementable) { template<class T> auto requires(T&& x) -> TICK_VALID( x++, ++x ); }; Then use it like this: static_assert(is_incrementable<T>(), "Not incrementable"); Also, you can validate the return of an expressions like this: TICK_TRAIT(is_equality_comparable) { template<class T, class U> auto requires(T&& x, U&& y) -> TICK_VALID( returns<bool>(x == y), returns<bool>(x != y) ); }; You can also match against another trait using placeholder expressions: TICK_TRAIT(is_incrementable) { template<class T> auto requires(T&& x) -> TICK_VALID( return<std::is_integral<_>>(x++), return<std::is_integral<_>>(++x) ); }; Any feedback would be appreciated. Also, I don't knw if there would be any interest in incorporating this into boost. Thanks, Paul
Le 22/05/14 18:23, paul Fultz a écrit :
Hi,
I developed a trait introspection library for C++14 based on Eric Niebler's concept checking, here:
https://github.com/pfultz2/Tick
So you can build a simple `is_incrementable` trait like this:
TICK_TRAIT(is_incrementable) { template<class T> auto requires(T&& x) -> TICK_VALID( x++, ++x ); };
Then use it like this:
static_assert(is_incrementable<T>(), "Not incrementable");
Also, you can validate the return of an expressions like this:
TICK_TRAIT(is_equality_comparable) { template<class T, class U> auto requires(T&& x, U&& y) -> TICK_VALID( returns<bool>(x == y), returns<bool>(x != y) ); };
You can also match against another trait using placeholder expressions:
TICK_TRAIT(is_incrementable) { template<class T> auto requires(T&& x) -> TICK_VALID( return<std::is_integral<_>>(x++), return<std::is_integral<_>>(++x) ); };
Any feedback would be appreciated. Also, I don't knw if there would be any interest in incorporating this into boost.
I like the framework Eric has developed. The description is quite close of what could be done at the language level. What is the added value of your library respect to Eric work? E.g. next follows the Addable concept checker 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 )); }; Best, Vicente
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<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<_>>(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-1... Sent from the Boost - Dev mailing list archive at Nabble.com.
pfultz2 wrote:
In Tick, we use placeholder expressions to define refinements from other traits. [...]
Sorry for the non-technical observation, but you do know that a tick is a parasite that spreads the nasty and often misdiagnosed Lyme disease, right? :-)
Yes, but the word tick has many meanings as well.(The tick is also a superhero). Its actually named tick because its almost like an acronym for the library name: Trait Introspection and Concept Kreator. -- View this message in context: http://boost.2283326.n4.nabble.com/Tick-Trait-instrospection-library-for-C-1... Sent from the Boost - Dev mailing list archive at Nabble.com.
-----Original Message----- From: Boost [mailto:boost-bounces@lists.boost.org] On Behalf Of Peter Dimov Sent: 31 May 2014 18:01 To: boost@lists.boost.org Subject: Re: [boost] Tick: Trait instrospection library for C++14
pfultz2 wrote:
In Tick, we use placeholder expressions to define refinements from other traits. [...]
Sorry for the non-technical observation, but you do know that a tick is a parasite that spreads the nasty and often misdiagnosed Lyme disease, right? :-)
And lies in wait in the long grass waiting to bite you when you are not paying attention ;-) Paul
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<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<_>>(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
Ok, I can see the advantage of using macros, but please not too much.
Yes, of course, that is the only macro that is required, and only because without it can be error prone(perhaps there is another way, but I am unaware of).
I like concept::models. tick_traits doesn't mean nothing significant.
Yes, it probably would be better for me to use `models`.
Why do you need tick:ops?
This will bring in the query operations(such as `returns`, `has_type`, `has_template`, etc), plus also the placholders(so you can just use `_`,`_1`,`_2`).
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.
No, the classes and macros are really orthogonal. The only complexity added to the classes is that they store the base tratis and refinements so that you can see which of the base traits have failed. Which is completely unrelated to the macros.
I really prefer the last one (even if the 42is magic). I need to admit that the macro TICK_VALID could be useful.
Except using just the `concepts::valid_expr` is problematic. If someone forgets to put in the hack(which is easy to do), it can lead to unintended compile errors. That is why the `TICK_VALID` macro is required.
BTW, how do you know how many template parameters has the concept?
That comes from how many parameters are used in the requires function.
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.
No need for placeholders in Eric version.
Actually, Eric does use placeholders as well, I'm not sure the reason. However, the reason I use placeholder expressions is better operability with current type traits. Eric's versions requires wrapping it in another class. Also, another reason placeholders are used is overall simplicity. They are all type traits(ie `std::integral_constant<bool, B>` actually), and they work with other traits. The classes with the `requires` functions are just implementation details, this is to help avoid confusion about which class is to be used where. So, for example, if I want to refine the `is_incrementable` all I have to do is write `refines<is_incrementable<_>>` whereas in Eric's version if I write `refines<Incrementble>` it could lead to a compile error. Where I should have written `refines<concepts::Incrementable>` instead.
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>{}) )); };
This doesn't do the same thing. Perhaps its due to my poorly named example. It should be called `has_fusion_foo`, like this: TICK_TRAIT(has_fusion_foo) { template<class T> auto requires(T&& x) -> TICK_VALID( returns<boost::fusion::is_sequence<_>>(x.foo()) ); }; Using Eric's framework: struct FusionSequence { template<class T> auto requires(T&& x) -> decltype(concepts::valid_expr( concepts::is_true(boost::fusion::is_sequence<T>{}) )); }; struct FusionFoo { template<class T> auto requires(T&& x) -> decltype(concepts::valid_expr( concept::model_of<FusionSequence>(x.foo()) )); }; template<class T> using has_fusion_foo = concept::model<T, FusionFoo>;
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<T>() + std::declval<U>())>; This allows for better reusability and composability.
I prefer the concept::models<Concept, Model> expression to the trait expression tick::trait<isConcept(Model)>.
Maybe I might blend the two together and have `tick::models<Concept(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.
A non-macro version of the library is already provided with the exception of `TICK_VALID`. The only reason a non-macro version of `TICK_VALID` is not provided is because I feel it can be too error-prone.
What about working with Eric? I'm sure he would be happy to don't maintain the concepts part of range-v3.
That could be possible, but I haven't heard a response back from him. He is currently focusing on his range library right now, but I am sure he could provide very useful feedback as well.
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.
Thanks, and thanks for the feedback. -- View this message in context: http://boost.2283326.n4.nabble.com/Tick-Trait-instrospection-library-for-C-1... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 31/05/14 23:36, pfultz2 a écrit :
Ok, I can see the advantage of using macros, but please not too much. Yes, of course, that is the only macro that is required, and only because without it can be error prone(perhaps there is another way, but I am unaware of).
I like concept::models. tick_traits doesn't mean nothing significant. Yes, it probably would be better for me to use `models`.
Why do you need tick:ops? This will bring in the query operations(such as `returns`, `has_type`, `has_template`, etc), plus also the placholders(so you can just use `_`,`_1`,`_2`). 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.
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. No, the classes and macros are really orthogonal. The only complexity added to the classes is that they store the base tratis and refinements so that you can see which of the base traits have failed. Which is completely unrelated to the macros. 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)
I really prefer the last one (even if the 42is magic). I need to admit that the macro TICK_VALID could be useful. Except using just the `concepts::valid_expr` is problematic. If someone forgets to put in the hack(which is easy to do), it can lead to unintended compile errors. That is why the `TICK_VALID` macro is required.
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.
BTW, how do you know how many template parameters has the concept? That comes from how many parameters are used in the requires function.
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<_>|)|
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 need for placeholders in Eric version. Actually, Eric does use placeholders as well, I'm not sure the reason. Yes, I see them also. However, the reason I use placeholder expressions is better operability with current type traits. Eric's versions requires wrapping it in another class. Yes, this allows you to write
|TICK_TRAIT(is_incrementable, std::is_integral<_>)|
Also, another reason placeholders are used is overall simplicity. They are all type traits(ie `std::integral_constant<bool, B>` actually), and they work with other traits. The classes with the `requires` functions are just implementation details, this is to help avoid confusion about which class is to be used where. So, for example, if I want to refine the `is_incrementable` all I have to do is write `refines<is_incrementable<_>>` whereas in Eric's version if I write `refines<Incrementble>` it could lead to a compile error. Where I should have written `refines<concepts::Incrementable>` instead.
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.
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>{}) )); }; This doesn't do the same thing. Perhaps its due to my poorly named example.
Agreed.
It should be called `has_fusion_foo`, like this:
TICK_TRAIT(has_fusion_foo) { template<class T> auto requires(T&& x) -> TICK_VALID( returns<boost::fusion::is_sequence<_>>(x.foo()) ); }; Please could you explain what
Using Eric's framework:
struct FusionSequence { template<class T> auto requires(T&& x) -> decltype(concepts::valid_expr( concepts::is_true(boost::fusion::is_sequence<T>{}) )); };
struct FusionFoo { template<class T> auto requires(T&& x) -> decltype(concepts::valid_expr( concept::model_of<FusionSequence>(x.foo()) )); }; This check the function member foo returns a model of a Fusion sequence, isn't it? template<class T> using has_fusion_foo = concept::model<T, FusionFoo>;
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<T>() + std::declval<U>())>;
This allows for better reusability and composability. All good reasons, but as always is a taste question. Is there something
returns<boost::fusion::is_sequence<_>>(x.foo()) checks? That there is a function member foo and ... that prevents using them?
I prefer the concept::models<Concept, Model> expression to the trait expression tick::trait<isConcept(Model)>. Maybe I might blend the two together and have `tick::models<Concept(Model)>`.
Why not.
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. A non-macro version of the library is already provided with the exception of `TICK_VALID`. The only reason a non-macro version of `TICK_VALID` is not provided is because I feel it can be too error-prone.
See above. Best, Vicente
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<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<_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_<Int() == 0>>() ); } template<int N> using is_zero = tick::trait<Zeroed(int_<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<_>>(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<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-1... Sent from the Boost - Dev mailing list archive at Nabble.com.
Le 22/05/14 18:23, paul Fultz a écrit :
Hi,
I developed a trait introspection library for C++14 based on Eric Niebler's concept checking, here:
https://github.com/pfultz2/Tick
So you can build a simple `is_incrementable` trait like this:
TICK_TRAIT(is_incrementable) { template<class T> auto requires(T&& x) -> TICK_VALID( x++, ++x ); };
Then use it like this:
static_assert(is_incrementable<T>(), "Not incrementable");
Also, you can validate the return of an expressions like this:
TICK_TRAIT(is_equality_comparable) { template<class T, class U> auto requires(T&& x, U&& y) -> TICK_VALID( returns<bool>(x == y), returns<bool>(x != y) ); };
You can also match against another trait using placeholder expressions:
TICK_TRAIT(is_incrementable) { template<class T> auto requires(T&& x) -> TICK_VALID( return<std::is_integral<_>>(x++), return<std::is_integral<_>>(++x) ); };
Any feedback would be appreciated. Also, I don't knw if there would be any interest in incorporating this into boost.
I recommend that you do not use identifiers 'concept' or 'requires'. They are likely to become keywords in C++17. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4040.pdf. If added, this will break your code and negatively surprise your users. Worse, if your library becomes popular, it may prevent the addition of the keywords, and C++ will have to resort to uglier names like "concept_def" and "concept_requires". Regards, 7rzej
Le 02/06/14 15:35, Andrzej Krzemienski a écrit :
I recommend that you do not use identifiers 'concept' or 'requires'. They are likely to become keywords in C++17. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4040.pdf. If added, this will break your code and negatively surprise your users. Worse, if your library becomes popular, it may prevent the addition of the keywords, and C++ will have to resort to uglier names like "concept_def" and "concept_requires".
Eric Range-V3 library uses also a requires function, and a concepts namespace (but some one was suggesting to move to a nmespace concept :(. Some suggestions requires_ and concept_. Vicente
I recommend that you do not use identifiers 'concept' or 'requires'.
Ok, I've updated and renamed `requires` to `requires_`. I don't use the `concept` keyword at all. I also renamed `tick::trait` to `tick::models`. -- View this message in context: http://boost.2283326.n4.nabble.com/Tick-Trait-instrospection-library-for-C-1... Sent from the Boost - Dev mailing list archive at Nabble.com.
participants (7)
-
Andrzej Krzemienski
-
Nat Goodspeed
-
Paul A. Bristow
-
paul Fultz
-
Peter Dimov
-
pfultz2
-
Vicente J. Botet Escriba