Tick: Trait instrospection and concepts library now for C++11

Hi, I've just update the Tick library to support C++11 compilers including support for gcc 4.6: https://github.com/pfultz2/Tick To support gcc 4.6 an additional `TICK_RETURNS` macro was added. So while this can be written for gcc 4.7 or later: TICK_TRAIT(is_incrementable) { template<class T> auto requires_(T&& x) -> TICK_VALID( returns<int>(x++) ); }; This can be written in order to support gcc 4.6: TICK_TRAIT(is_incrementable) { template<class T> auto requires_(T&& x) -> TICK_VALID( TICK_RETURNS(x++, int) ); }; Of course, the macro is optional and only needed to support older compilers. Any other feedback would be appreciated. Thanks, Paul

Le 05/06/14 07:53, paul Fultz a écrit :
Hi,
I've just update the Tick library to support C++11 compilers including support for gcc 4.6:
https://github.com/pfultz2/Tick
To support gcc 4.6 an additional `TICK_RETURNS` macro was added. So while this can be written for gcc 4.7 or later:
TICK_TRAIT(is_incrementable) { template<class T> auto requires_(T&& x) -> TICK_VALID( returns<int>(x++) ); };
This can be written in order to support gcc 4.6:
TICK_TRAIT(is_incrementable) { template<class T> auto requires_(T&& x) -> TICK_VALID( TICK_RETURNS(x++, int) ); };
Of course, the macro is optional and only needed to support older compilers.
Any other feedback would be appreciated.
Hi, Does your library define just traits or try to emulate Concept Lite? In this post concepts@isocpp.org "[concepts] Can Concepts Lite be used to conditionally disable a copy constructor?" there are a couple of questions related to your library. * Can these traits be used to conditionally disable a constructors? an assignment? class NonCopyable { NonCopyable(NonCopyable const&) = delete; }; template <typename T> class wrapper { wrapper(const wrapper&) requires std::is_copy_constructible<T>::value {} }; * Can non-template members of template class be overloaded by only requires clause? I mean is the following examples correct (assuming that C1 and C2 are concepts): template<typename T> struct A { A(const A&) requires C1<T> { /*...*/ }; A(const A&) requires C2<T> { /*...*/ }; }; How the refinements plays on this concern? What are the limitations respect ConceptLite? Best, Vicente

Does your library define just traits or try to emulate Concept Lite?
I'm not quite sure what you are asking. The library doesn't do everything that Concepts Lite does. It lets you define concept-like predicates(commonly called a type trait in C++) in a similiar manner as Concepts Lite does. It does do template constraints as well, but this is just done using `enable_if`. Also, it won't resolve ambiguity between overloads like Concepts Lite does. However, I am thinking about implementing a mechanism for this. Perhaps something like this: struct advance1 { template<class Iterator> TICK_OVERLOAD_REQUIRES(is_random_access_iterator<Iterator>); template<class Iterator> void operator()(Iterator& it, int n) const { it += n; } }; struct advance2 { template<class Iterator> TICK_OVERLOAD_REQUIRES(is_input_iterator<Iterator>); template<class Iterator> void operator()(Iterator& it, int n) const { while (n--) ++it; } }; tick::overload<advance1, advance2> advance = {}; However, this is fairly verbose even with the help of macros. It just might be easier to add some `not`s to the requires clause. Now, non-template members can be disabled, but they need to be made templated, like this: template<class Base> struct A : Base { template<class Self=A, TICK_REQUIRES(is_incrementable<Self>()) void increment() { ++(*this); } }; However, the library cannot be used with special members(such as copy constructors, move constructors, and assignment operators). This is a limitation of the language, since these special members cannont ever be templates. There might be a way to workaround the issue using universal references. I haven't really looked into it. Thanks, Paul -- View this message in context: http://boost.2283326.n4.nabble.com/Tick-Trait-instrospection-and-concepts-li... Sent from the Boost - Dev mailing list archive at Nabble.com.

Le 07/06/14 07:18, pfultz2 a écrit :
Does your library define just traits or try to emulate Concept Lite? I'm not quite sure what you are asking. The library doesn't do everything that Concepts Lite does. It lets you define concept-like predicates(commonly called a type trait in C++) in a similiar manner as Concepts Lite does. It does do template constraints as well, but this is just done using `enable_if`. Also, it won't resolve ambiguity between overloads like Concepts Lite does. However, I am thinking about implementing a mechanism for this. Perhaps something like this:
struct advance1 { template<class Iterator> TICK_OVERLOAD_REQUIRES(is_random_access_iterator<Iterator>);
template<class Iterator> void operator()(Iterator& it, int n) const { it += n; }
};
struct advance2 { template<class Iterator> TICK_OVERLOAD_REQUIRES(is_input_iterator<Iterator>);
template<class Iterator> void operator()(Iterator& it, int n) const { while (n--) ++it; }
};
tick::overload<advance1, advance2> advance = {};
However, this is fairly verbose even with the help of macros. It just might be easier to add some `not`s to the requires clause. Have you read the Concept-Based Overloadingin Eric's blog [1]? Eric uses a dispatching schema on which the Concept definition is the tag dispatcher.
// iterator_concept template<typename T> using iterator_concept = concepts::most_refined< typelist< concepts::RandomAccessIterator, concepts::BidirectionalIterator, concepts::ForwardIterator, concepts::InputIterator, concepts::WeakInputIterator>, T>; template<typename T> using iterator_concept_t = meta_apply<iterator_concept, T>; |// Random-access iterators go here| |template||<||typename| |RndIt, ||typename| |Diff>| |void| |advance_impl(RndIt & it, Diff d,| |||ranges::concepts::RandomAccessIterator)| |{| |||it += d;| |}| |// All other iterator types go here| |template||<||typename| |InIt, ||typename| |Diff>| |void| |advance_impl(InIt & it, Diff d,| |||ranges::concepts::InputIterator)| |{| |||for||(; d != 0; --d)| |||++it;| |}| |template||<||typename| |InIt, ||typename| |Diff,| |||CONCEPT_REQUIRES(ranges::InputIterator<InIt>() &&| |||ranges::Integral<Diff>())>| |void| |advance(InIt it, Diff d)| |{| |||advance_impl(it, d, ranges::iterator_concept_t<InIt>{});| |}| This is not perfect, but is the best we can do without concepts integrated in the language. What about adding this most_refined trait to your library? it would help the user to do this tag dispatching?
Now, non-template members can be disabled, but they need to be made templated, like this:
template<class Base> struct A : Base { template<class Self=A, TICK_REQUIRES(is_incrementable<Self>()) void increment() { ++(*this); } }; This is not a good idea. The default will be checked independently of whether increment is used or not, isn't it? However, the library cannot be used with special members(such as copy constructors, move constructors, and assignment operators). This is a limitation of the language, since these special members cannont ever be templates. I agree that this is a language limitation. The problem is not if they are templates or not. The problem is when the constraints are checked.
There might be a way to workaround the issue using universal references. I haven't really looked into it. No, this is a real limitation.
Best, Vicente. [1] http://ericniebler.com/2013/11/23/concept-checking-in-c11/

Have you read the Concept-Based Overloadingin Eric's blog [1]? Eric uses a dispatching schema on which the Concept definition is the tag dispatcher.
Yes, I have, however, tag dispatching doesn't work with specialization(neither does concept-based overloading, apparently). After thinking about it more, a simpler way is to just use conditional overloading instead, which would work something like this: struct advance1 { template<class Iterator, TICK_REQUIRES(is_random_access_iterator<Iterator>())> void operator()(Iterator& it, int n) const { it += n; } }; struct advance2 { template<class Iterator, TICK_REQUIRES(is_input_iterator<Iterator>())> void operator()(Iterator& it, int n) const { while (n--) ++it; } }; tick::conditional<advance1, advance2> advance = {}; The order the functions are placed matter, think of it like this pseudo- syntax: template<class Iterator> void advance(Iterator& it, int n) if (is_random_access_iterator<Iterator>()) { it += n; } else if (is_input_iterator<Iterator>()) { while (n--) ++it; } Perhaps, some macros could help to reduce the boilerplate.
This is not a good idea. The default will be checked independently of whether increment is used or not, isn't it?
It gets checked when increment gets called(at instantiation). If `is_incrementable` is false, it will cause substitution failure. Paul -- View this message in context: http://boost.2283326.n4.nabble.com/Tick-Trait-instrospection-and-concepts-li... Sent from the Boost - Dev mailing list archive at Nabble.com.
participants (3)
-
paul Fultz
-
pfultz2
-
Vicente J. Botet Escriba