Proposal for New Template Metafunction 'is_iterator'

Is there a utility in Boost which determines at compile time whether or not a given type meets the most basic requirements for an iterator, without generating an error if the test fails? (This last part is crucial, so concept checks are insufficient.) I recently needed something like this, and I couldn't seem to find it in any of the libraries, so I went ahead and wrote a template metafunction called 'is_iterator' which gets the job done. It uses the same SFINAE techniques as the enable_if library. The declaration looks like this: template <typename T> struct is_iterator; The 'is_iterator' struct template is a model of the "integral constant expression" concept. Thus, 'is_iterator<T>::value' evaluates to true if-and-only-if type T defines iterator traits as well as overloaded operators with the following signatures: boost::iterator_reference<T>::type operator*(void) const; boost::iterator_pointer<T>::type operator->(void) const; T & operator++(void); T operator++(int); These seem to be the minimal requirements (i.e. Readable and Incrementable) according to the New Iterator Concepts. Ideally, T would also have to be a Copy Constructible type, but I don't know of a reliable way to detect this. Originally, i needed the ability to determine at compile time whether or not a given type meets the requirements for a Readable Lvalue iterator. The 'is_readable_iterator' and 'is_lvalue_iterator' template metafunctions work fine for this purpose, except that they both generate an error when passed a template argument that is not an iterator at all. Assuming I'm correct in stating that a utility like 'is_iterator' does not already exist in Boost, is there any interest in such a utility? Thanks, Alex

"Thomas Witt" <witt@acm.org> wrote in message news:cgal5p$lb4$1@sea.gmane.org...
How do you implement that?
Thomas, I'm using the SFINAE principle. I have two member functions called 'test' in 'struct detail::is_iterator_impl', where one of them is a template: static char (& test(...))[2]; template <typename T> static char test( T *, type_tag<typename T::value_type> * = 0, type_tag<typename T::reference> * = 0, type_tag<typename T::pointer> * = 0, type_tag<typename T::difference_type> * = 0, type_tag<typename T::iterator_category> * = 0, star_op_tag<T, &T::operator* > * = 0, arrow_op_tag<T, &T::operator-> > * = 0, prefix_op_tag<T, &T::operator++ > * = 0, postfix_op_tag<T, &T::operator++ > * = 0 ); Then in 'is_iterator', I define 'value' as follows: BOOST_STATIC_CONSTANT( value_type, value = sizeof(detail::is_iterator_impl::test((T *) 0)) == 1 ); Thus, if T meets the requirements for an iterator, argument deduction succeeds and an instantiation of the 'test' function template is added to the set of candidate functions. And since T * is a better match than ..., this function is the one that gets chosen during overload resolution. Then 'value' evaluates to true. Otherwise, argument deduction fails, and the first function gets chosen instead because it's the only candidate. Then 'value' evaluates to false. Of course, this implementation will not work for pointers, so I provide a partial template specialization for this case. (Please see my response to Jonathan for details.) As an alternative, I considered using Boost iterator traits instead of constructs like 'typename T::value_type', since iterator traits handle pointers correctly. But iterator traits don't include operators, so a partial template specialization would still be required. Lastly, Jonathan pointed out that these operators can be implemented as non-member functions. I am working on a solution that will work when this is the case. Thanks, Alex

"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:cgapen$tfi$1@sea.gmane.org...
"Thomas Witt" <witt@acm.org> wrote in message news:cgal5p$lb4$1@sea.gmane.org...
How do you implement that?
Thomas,
I'm using the SFINAE principle. I have two member functions called
'test' in
'struct detail::is_iterator_impl', where one of them is a template:
static char (& test(...))[2];
template <typename T> static char test( T *, type_tag<typename T::value_type> * = 0, type_tag<typename T::reference> * = 0, type_tag<typename T::pointer> * = 0, type_tag<typename T::difference_type> * = 0, type_tag<typename T::iterator_category> * = 0,
Note that boost already has a facility for detecting member types. See boost/mpl/aux_/has_xxx.hpp. Jonathan

"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:cgajri$ils$1@sea.gmane.org...
Is there a utility in Boost which determines at compile time whether or not a given type meets the most basic requirements for an iterator, without generating an error if the test fails?
Currently there is detail::is_incrementable, which works well if you only need to distinguish between iterators and very different types. For instance, I would guess that very few if any containers are incrementable (I'd be happy to be proved wrong here), so that if you want define a function which behaves differently for container and iterators you can use is_incrementable.
The 'is_iterator' struct template is a model of the "integral constant expression" concept. Thus, 'is_iterator<T>::value' evaluates to true if-and-only-if type T defines iterator traits as well as overloaded operators with the following signatures:
boost::iterator_reference<T>::type operator*(void) const; boost::iterator_pointer<T>::type operator->(void) const; T & operator++(void); T operator++(int);
Don't forget pointers. Also these operators can also be defined as non-member functions. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:cgal34$l63$1@sea.gmane.org...
Currently there is detail::is_incrementable, which works well if you only need to distinguish between iterators and very different types. For instance, I would guess that very few if any containers are incrementable (I'd be happy to be proved wrong here), so that if you want define a function which behaves differently for container and iterators you can use is_incrementable.
In this case, I need to know whether or not type T is dereferenceable (and more generally, whether or not it's an iterator). I have a small template metaprogram which determines how an instance of type T should be treated. If it's an iterator, I want to dereference it. Otherwise, I don't.
Don't forget pointers. Also these operators can also be defined as non-member functions.
Pointers are fairly easy to handle. I forgot to mention that I provide a partial template specialization for this case: for any type T, 'is_iterator<T *>::value' evaluates to true. You bring up an important point about operators defined as non-member functions. My current implementation doesn't handle this case, but I may yet be able to fix that. :-) Thanks for the feedback, Alex

"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:cganiq$q27$1@sea.gmane.org...
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:cgal34$l63$1@sea.gmane.org...
Currently there is detail::is_incrementable, which works well if
you
only need to distinguish between iterators and very different types. For instance, I would guess that very few if any containers are incrementable (I'd be happy to be proved wrong here), so that if you want define a function which behaves differently for container and iterators you can use is_incrementable.
In this case, I need to know whether or not type T is dereferenceable
I've attached a naively modified version of David Abraham's is_incrementable as a first attempt at is_dereferenceable. There may be all sorts of subtle issues I haven't though of.
(and more generally, whether or not it's an iterator).
I have a small template metaprogram which determines how an instance of type T should be
Why? When you write a template, you always have some idea of the range of types for which the template should be instantiated. It's rarely instantiable for all types. Given this, all you need is a way to separate iterators from the other candidate template arguments. treated. If
it's an iterator, I want to dereference it. Otherwise, I don't.
Don't forget pointers. Also these operators can also be defined as non-member functions.
Pointers are fairly easy to handle.
True.
You bring up an important point about operators defined as non-member functions. My current implementation doesn't handle this case, but I may yet be able to fix that. :-)
Try the attachment.
Thanks for the feedback,
Alex
begin 666 is_dereferenceable.hpp M+R\@3F%I=F5L>2!M;V1I9FEE9"!V97)S:6]N(&]F(&)O;W-T+V1E=&%I;"]I M<U]I;F-R96UE;G1A8FQE+FAP<"P@87,@9FER<W0@#0HO+R!A='1E;7!T(&%T M(&ES7V1E<F5F97)E;F-E86)L92P@8GD@2F]N871H86X@5'5R:V%N:7,N#0H- M"B\O($-O<'ER:6=H="!$879I9"!!8G)A:&%M<R R,# T+B!5<V4L(&UO9&EF M:6-A=&EO;B!A;F0@9&ES=')I8G5T:6]N(&ES#0HO+R!S=6)J96-T('1O('1H M92!";V]S="!3;V9T=V%R92!,:6-E;G-E+"!697)S:6]N(#$N,"X@*%-E92!A M8V-O;7!A;GEI;F<-"B\O(&9I;&4@3$E#14Y315\Q7S N='AT(&]R(&-O<'D@ M870@:'1T<#HO+W=W=RYB;V]S="YO<F<O3$E#14Y315\Q7S N='AT*0T*(VEF M;F1E9B!)4U]$15)%1D5214Y#14%"3$5?2%!07TE.0TQ51$5$#0HC(&1E9FEN M92!)4U]$15)%1D5214Y#14%"3$5?2%!07TE.0TQ51$5$#0H-"B,@:6YC;'5D M92 \8F]O<W0O='EP95]T<F%I=',O<F5M;W9E7V-V+FAP<#X-"B,@:6YC;'5D M92 \8F]O<W0O;7!L+V)O;VPN:'!P/@T*(R!I;F-L=61E(#QB;V]S="]D971A M:6PO=V]R:V%R;W5N9"YH<' ^#0H-"FYA;65S<&%C92!B;V]S="![(&YA;65S M<&%C92!D971A:6P@>R -"@T*+R\@:7-?9&5R969E<F5N8V5A8FQE/%0^(&UE M=&%F=6YC=&EO;@T*+R\-"B\O(%)E<75I<F5S.B!':79E;B!X(&]F('1Y<&4@ M5"8L(&EF('1H92!E>'!R97-S:6]N("IX(&ES('=E;&PM9F]R;65D#0HO+R!I M="!M=7-T(&AA=F4@8V]M<&QE=&4@='EP93L@;W1H97)W:7-E+"!I="!M=7-T M(&YE:71H97(@8F4@86UB:6=U;W5S#0HO+R!N;W(@=FEO;&%T92!A8V-E<W,N M#0H-"B\O(%1H:7,@;F%M97-P86-E(&5N<W5R97,@=&AA="!!1$P@9&]E<VXG M="!M97-S('1H:6YG<R!U<"X-"FYA;65S<&%C92!I<U]D97)E9F5R96YC96%B M;&5?#0I[#0H@("\O(&$@='EP92!R971U<FYE9"!F<F]M(&]P97)A=&]R*B!W M:&5N(&YO(&EN8W)E;65N="!I<R!F;W5N9"!I;B!T:&4-"B @+R\@='EP92=S M(&]W;B!N86UE<W!A8V4-"B @<W1R=6-T('1A9R![?3L-"B @#0H@("\O(&%N M>2!S;V%K<R!U<"!I;7!L:6-I="!C;VYV97)S:6]N<R!A;F0@;6%K97,@=&AE M(&9O;&QO=VEN9PT*(" O+R!O<&5R871O<BH@;&5S<RUP<F5F97)R960@=&AA M;B!A;GD@;W1H97(@<W5C:"!O<&5R871O<B!T:&%T#0H@("\O(&UI9VAT(&)E M(&9O=6YD('9I82!!1$PN#0H@('-T<G5C="!A;GD@>R!T96UP;&%T92 \8VQA M<W,@5#X@86YY*%0@8V]N<W0F*3L@?3L-"@T*(" O+R!4:&ES(&ES(&$@;&%S M="UR97-O<G0@;W!E<F%T;W(J(&9O<B!W:&5N(&YO;F4@;W1H97(@:7,@9F]U M;F0-"B @=&%G(&]P97)A=&]R*BAA;GD@8V]N<W0F*3L-"@T*(R!I9B!"3T]3 M5%]73U)+05)/54Y$*%]?35=%4DM37U\L($)/3U-47U1%4U1%1%]!5"@P>#,R M,#(I*2!<#0H@(" @?'P@0D]/4U1?5T]22T%23U5.1"A"3T]35%]-4U9#+" \ M/2 Q,S P*0T*(R @9&5F:6YE($)/3U-47V-O;6UA*&$L8BD@*&$I#0HC(&5L M<V4@#0H@("\O($EN(&-A<V4@86X@;W!E<F%T;W(J(&ES(&9O=6YD('1H870@ M<F5T=7)N<R!V;VED+"!W92=L;"!U<V4@*G@L, T*("!T86<@;W!E<F%T;W(L M*'1A9RQI;G0I.R @#0HC("!D969I;F4@0D]/4U1?8V]M;6$H82QB*2 H82QB M*0T*(R!E;F1I9B -"B @#0H@("\O('1W;R!C:&5C:R!O=F5R;&]A9',@:&5L M<"!U<R!I9&5N=&EF>2!W:&EC:"!O<&5R871O<BH@=V%S('!I8VME9 T*("!C M:&%R("@F(&-H96-K*'1A9RD@*5LR73L-"B @#0H@('1E;7!L871E(#QC;&%S M<R!4/@T*("!C:&%R(&-H96-K*%0@8V]N<W0F*3L-"B @#0H-"B @=&5M<&QA M=&4@/&-L87-S(%0^#0H@('-T<G5C="!I;7!L#0H@('L-"B @(" @('-T871I M8R!T>7!E;F%M92!R96UO=F5?8W8\5#XZ.G1Y<&4F('@[#0H-"B @(" @($)/ M3U-47U-4051)0U]#3TY35$%.5"@-"B @(" @(" @("!B;V]L#0H@(" @(" @ M("P@=F%L=64@/2!S:7IE;V8H:7-?9&5R969E<F5N8V5A8FQE7SHZ8VAE8VLH M0D]/4U1?8V]M;6$H*G@L,"DI*2 ]/2 Q#0H@(" @(" I.PT*("!].PT*?0T* M#0HC('5N9&5F($)/3U-47V-O;6UA#0H-"G1E;7!L871E(#QC;&%S<R!4/@T* M<W1R=6-T(&ES7V1E<F5F97)E;F-E86)L90T*(" Z(&UP;#HZ8F]O;%\\(#HZ M8F]O<W0Z.F1E=&%I;#HZ:7-?9&5R969E<F5N8V5A8FQE7SHZ:6UP;#Q4/CHZ M=F%L=64^#0I[#0I].PT*#0I]?2 O+R!N86UE<W!A8V4@8F]O<W0Z.F1E=&%I M; T*#0HC96YD:68@+R\@25-?1$52149%4D5.0T5!0DQ%7TA04%])3D-,541% #1 T* ` end

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:cgapf9$tgn$1@sea.gmane.org...
"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:cganiq$q27$1@sea.gmane.org...
I've attached a naively modified version of David Abraham's is_incrementable as a first attempt at is_dereferenceable. There may be all sorts of subtle issues I haven't though of.
Ok I'll check it out. Maybe it's more robust than the SFINAE approach to recognizing overloaded operators which I'm using right now.
(and more generally, whether or not it's an iterator).
Why? When you write a template, you always have some idea of the range of types for which the template should be instantiated. It's rarely instantiable for all types. Given this, all you need is a way to separate iterators from the other candidate template arguments.
Ok, so I have a class Ptr_Wrapper that is a wrapper around a pointer to an object of arbitrary type, and it has a template constructor which accepts any kind of iterator. The constructor initializes the pointer member with the address of the object that the iterator refers to. Now, I don't want is_convertible<T, Ptr_Wrapper> to be true for any type T, so I want to use the enable_if library to eliminate specializations of the template constructor where the type passed in is not a Readable Lvalue iterator with the correct pointer type. But again, I can't just use is_readable_iterator<T> and is_lvalue_iterator<T>, because either one will cause a compile error if T is not an iterator. I don't want there to be a compile error in this case, because Ptr_Wrapper has another constructor which accepts a reference to an object an initializes the pointer member from that. I want this constructor to be chosen if a reference is passed instead of an iterator. Thus, in the template metaprogram that I use to decide whether or not to include a particular template specialization of the constructor in the set of candidate functions, I need to check that T is some kind of iterator before applying is_readable_iterator<T> and is_lvalue_iterator<T>. Otherwise, I may get an unwanted compile error. Thanks, Alex

"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:cgbhkt$599$1@sea.gmane.org...
Ok, so I have a class Ptr_Wrapper that is a wrapper around a pointer to an object of arbitrary type,
You mean Ptr_Wrapper is not a template? How does it remember the type of object it contains?
and it has a template constructor which accepts any kind of iterator. The constructor initializes the pointer member with the address of the object that the iterator refers to.
Now, I don't want is_convertible<T, Ptr_Wrapper> to be true for any type T, so I want to use the enable_if library to eliminate specializations of the template constructor where the type passed in is not a Readable Lvalue iterator with the correct pointer type. But again, I can't just use is_readable_iterator<T> and is_lvalue_iterator<T>, because either one will cause a compile error if T is not an iterator.
I don't want there to be a compile error in this case, because Ptr_Wrapper has another constructor which accepts a reference to an object an initializes the pointer member from that.
I want this constructor to be chosen if a reference is passed instead of an iterator.
Thus, in the template metaprogram that I use to decide whether or not to include a particular template specialization of the constructor in
Possibly dumb question: why not have this second overload take a pointer? the set
of candidate functions, I need to check that T is some kind of iterator before applying is_readable_iterator<T> and is_lvalue_iterator<T>. Otherwise, I may get an unwanted compile error.
Okay, assuming the second overload has to take a reference, this does sound like a case where you want to be able to treat almost any possible type. Realistically, how often would: template<typename T> Ptr_Wrapper( const T&, typename enable_if< is_incrementable<T> >::type* = 0, typename enable_if< is_dereferenceable<T> >::type* = 0 ); template<typename T> Ptr_Wrapper( T&, typename disable_if< is_incrementable<T> >::type* = 0, typename disable_if< is_dereferenceable<T>
::type* = 0 );
select the wrong overload? If it's fairly rare, you could add a disambiguation mechanism: Ptr_Wrapper pw = Ptr_Wrapper::from_object(x); or Ptr_Wrapper pw( disambiguation_func_with_better_name(x) ); Jonathan

"Alex Chovanec" <achovane@engin.umich.edu> writes:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:cgal34$l63$1@sea.gmane.org...
Currently there is detail::is_incrementable, which works well if you only need to distinguish between iterators and very different types. For instance, I would guess that very few if any containers are incrementable (I'd be happy to be proved wrong here), so that if you want define a function which behaves differently for container and iterators you can use is_incrementable.
In this case, I need to know whether or not type T is dereferenceable (and more generally, whether or not it's an iterator). I have a small template metaprogram which determines how an instance of type T should be treated. If it's an iterator, I want to dereference it. Otherwise, I don't.
Don't forget pointers. Also these operators can also be defined as non-member functions.
Pointers are fairly easy to handle. I forgot to mention that I provide a partial template specialization for this case: for any type T, 'is_iterator<T *>::value' evaluates to true.
You bring up an important point about operators defined as non-member functions. My current implementation doesn't handle this case, but I may yet be able to fix that. :-)
That's easy to fix; use the same technique as is_incrementable does. The traits are a harder problem. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"Alex Chovanec" <achovane@engin.umich.edu> writes:
Is there a utility in Boost which determines at compile time whether or not a given type meets the most basic requirements for an iterator, without generating an error if the test fails? (This last part is crucial, so concept checks are insufficient.)
I recently needed something like this, and I couldn't seem to find it in any of the libraries, so I went ahead and wrote a template metafunction called 'is_iterator' which gets the job done. It uses the same SFINAE techniques as the enable_if library.
The declaration looks like this:
template <typename T> struct is_iterator;
The 'is_iterator' struct template is a model of the "integral constant expression" concept. Thus, 'is_iterator<T>::value' evaluates to true if-and-only-if type T defines iterator traits
I don't believe that's detectable without generating an error. Don't forget that iterator_traits can be specialized; there's no reason the iterator has to contain members. Quick test: what is is_iterator<int*>::value?
as well as overloaded operators with the following signatures:
boost::iterator_reference<T>::type operator*(void) const; boost::iterator_pointer<T>::type operator->(void) const; T & operator++(void); T operator++(int);
These seem to be the minimal requirements (i.e. Readable and Incrementable) according to the New Iterator Concepts. Ideally, T would also have to be a Copy Constructible type, but I don't know of a reliable way to detect this.
There ain't none AFAICT.
Originally, i needed the ability to determine at compile time whether or not a given type meets the requirements for a Readable Lvalue iterator. The 'is_readable_iterator' and 'is_lvalue_iterator' template metafunctions work fine for this purpose, except that they both generate an error when passed a template argument that is not an iterator at all.
Assuming I'm correct in stating that a utility like 'is_iterator' does not already exist in Boost, is there any interest in such a utility?
Yes, if it's implementable. But I don't think it is. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:ullg6287u.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> writes:
The 'is_iterator' struct template is a model of the "integral constant expression" concept. Thus, 'is_iterator<T>::value' evaluates to true if-and-only-if type T defines iterator traits
I don't believe that's detectable without generating an error. Don't forget that iterator_traits can be specialized; there's no reason the iterator has to contain members. Quick test: what is is_iterator<int*>::value?
I don't really see why this a problem. 'is_iterator' can be specialized too, and that is what I have done for the case of pointers: template <typename T> struct is_iterator<T *> { typedef is_iterator type; typedef bool value_type; BOOST_STATIC_CONSTANT(value_type, value = true); }; So 'is_iterator<int *>::value' evaluates to true without generating a compile error, as it should. Alex

"Alex Chovanec" <achovane@engin.umich.edu> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:ullg6287u.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> writes:
The 'is_iterator' struct template is a model of the "integral constant expression" concept. Thus, 'is_iterator<T>::value' evaluates to true if-and-only-if type T defines iterator traits
I don't believe that's detectable without generating an error. Don't forget that iterator_traits can be specialized; there's no reason the iterator has to contain members. Quick test: what is is_iterator<int*>::value?
I don't really see why this a problem. 'is_iterator' can be specialized too, and that is what I have done for the case of pointers:
template <typename T> struct is_iterator<T *> { typedef is_iterator type; typedef bool value_type;
BOOST_STATIC_CONSTANT(value_type, value = true); };
So 'is_iterator<int *>::value' evaluates to true without generating a compile error, as it should.
*Any* iterator can be implemented the same way. An iterator class does not have to have a value_type member! -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacwmy7tw.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> writes:
So 'is_iterator<int *>::value' evaluates to true without generating a compile error, as it should.
*Any* iterator can be implemented the same way. An iterator class does not have to have a value_type member!
Ok, you bring up a valid point. I would like to suggest a possible solution to this problem, but the explanation is a bit long, so please bear with me. :-) Originally, I was using the following SFINAE approach in 'is_iterator' to detect member iterator traits: template <typename T> static char test( T *, type_tag<typename T::value_type> * = 0, type_tag<typename T::reference> * = 0, type_tag<typename T::pointer> * = 0, type_tag<typename T::difference_type> * = 0, type_tag<typename T::iterator_category> * = 0 ); static char (& test(...))[2]; I had considered using Boost iterator traits to do the same thing, like this: template <typename T> static char test( T *, type_tag<typename detail::iterator_traits<T>::value_type> * = 0, type_tag<typename detail::iterator_traits<T>::reference> * = 0, type_tag<typename detail::iterator_traits<T>::pointer> * = 0, type_tag<typename detail::iterator_traits<T>::difference_type> * = 0, type_tag<typename detail::iterator_traits<T>::iterator_category> * = 0 ); static char (& test(...))[2]; But I felt that the first approach was clearer, and I reasoned that it would be ok to rely on member traits if I also defined a partial template specialization to handle pointers. It wasn't until I read your last email that I tried the second approach and realized that it doesn't work. It's no surprise that attempting to instantiate 'iterator_traits<T>' causes a compile error when 'T' is not an iterator, but I had assumed that SFINAE would simply remove this overload of 'test' from the set of candidate functions. I see now that SFINAE doesn't work unless you have a valid type on the lefthand side of the '::' operator. So the problem is not that 'iterator_traits<T>::value_type' does not exist; the problem is that 'iterator_traits<T>' cannot be instantiated. But what if 'iterator_traits<T>' were defined in such a way that it can still be instantiated even if 'T' is not an iterator? If 'T' is not an iterator, then 'iterator_traits<T>' can simply be defined to be without any member types. This is easy to do with a little template metaprogramming. The modified primary template for Boost iterator traits might look something like this: template <typename T> struct iterator_traits : public mpl::if_< mpl::or_< has_member_iterator_traits<T>, is_pointer<T> >, std::iterator_traits<T>, empty_iterator_traits >::type {}; Here, 'has_member_iterator_traits' implements my first approach for detecting member iterator traits, and 'empty_iterator_traits' is just an empty type. This definition works for both iterators that have member traits and iterators that are pointers; and it can be specialized for other iterators just as easily as the existing 'iterator_traits'. Now, 'is_iterator' can be defined in terms of Boost iterator traits using the second approach described above. Since 'iterator_traits<T>' can always be instantiated, SFINAE will take care of the rest, and we won't be bothered with unwanted compile errors. What are your thoughts on this approach? Thanks, Alex Chovanec

"Alex Chovanec" <achovane@engin.umich.edu> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacwmy7tw.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> writes:
So 'is_iterator<int *>::value' evaluates to true without generating a compile error, as it should.
*Any* iterator can be implemented the same way. An iterator class does not have to have a value_type member!
Ok, you bring up a valid point. I would like to suggest a possible solution to this problem, but the explanation is a bit long, so please bear with me. :-)
Originally, I was using the following SFINAE approach in 'is_iterator' to detect member iterator traits:
template <typename T> static char test( T *, type_tag<typename T::value_type> * = 0, type_tag<typename T::reference> * = 0, type_tag<typename T::pointer> * = 0, type_tag<typename T::difference_type> * = 0, type_tag<typename T::iterator_category> * = 0 ); static char (& test(...))[2];
I had considered using Boost iterator traits to do the same thing, like this:
template <typename T> static char test( T *, type_tag<typename detail::iterator_traits<T>::value_type> * = 0, type_tag<typename detail::iterator_traits<T>::reference> * = 0, type_tag<typename detail::iterator_traits<T>::pointer> * = 0, type_tag<typename detail::iterator_traits<T>::difference_type> * = 0, type_tag<typename detail::iterator_traits<T>::iterator_category> * = 0 ); static char (& test(...))[2];
But I felt that the first approach was clearer, and I reasoned that it would be ok to rely on member traits if I also defined a partial template specialization to handle pointers.
It wasn't until I read your last email that I tried the second approach and realized that it doesn't work. It's no surprise that attempting to instantiate 'iterator_traits<T>' causes a compile error when 'T' is not an iterator, but I had assumed that SFINAE would simply remove this overload of 'test' from the set of candidate functions. I see now that SFINAE doesn't work unless you have a valid type on the lefthand side of the '::' operator.
So the problem is not that 'iterator_traits<T>::value_type' does not exist; the problem is that 'iterator_traits<T>' cannot be instantiated. But what if 'iterator_traits<T>' were defined in such a way that it can still be instantiated even if 'T' is not an iterator? If 'T' is not an iterator, then 'iterator_traits<T>' can simply be defined to be without any member types. This is easy to do with a little template metaprogramming.
No it isn't. A valid iterator may have a specialized std::iterator_traits. We can't change std::iterator_traits.
The modified primary template for Boost iterator traits might look something like this:
template <typename T> struct iterator_traits : public mpl::if_< mpl::or_< has_member_iterator_traits<T>, is_pointer<T> >, std::iterator_traits<T>, empty_iterator_traits >::type {};
Here, 'has_member_iterator_traits' implements my first approach for detecting member iterator traits, and 'empty_iterator_traits' is just an empty type. This definition works for both iterators that have member traits and iterators that are pointers; and it can be specialized for other iterators just as easily as the existing 'iterator_traits'.
Now, 'is_iterator' can be defined in terms of Boost iterator traits using the second approach described above. Since 'iterator_traits<T>' can always be instantiated, SFINAE will take care of the rest, and we won't be bothered with unwanted compile errors.
What are your thoughts on this approach?
Believe me, I've been over and over this one. There's no solution in the current language. Another one is is_assignable. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:ullg6287u.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> writes:
Assuming I'm correct in stating that a utility like 'is_iterator' does not already exist in Boost, is there any interest in such a utility?
Yes, if it's implementable. But I don't think it is.
Isn't the most fundamental problem that even if you could test for all the valid expressions you'll never be able to guarantee the semantics? So there will never be a perfect 'is_iterator'. In practice, one doesn't need to test for all the valid expressions. Using is_pointer, is_incrementable, is_dereferenceable, etc. will usually be sufficient to distinguish iterators from whatever other types you're trying to treat polymorphically with iterators. And you can always provide an explicit disambiguation mechanism in the (hopefully rare) case that a type is accidentally classified as an iterator. Am I overlooking some important use case? Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:cgbgtn$49l$1@sea.gmane.org...
"David Abrahams" <dave@boost-consulting.com> wrote in message news:ullg6287u.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> writes:
Assuming I'm correct in stating that a utility like
'is_iterator'
does not
already exist in Boost, is there any interest in such a utility?
Yes, if it's implementable. But I don't think it is.
Isn't the most fundamental problem that even if you could test for all the valid expressions you'll never be able to guarantee the semantics? So there will never be a perfect 'is_iterator'.
Okay, I guess this is a bad argument. Anyone who specializes iterator_traits for a non-iterator is a liar and deserves what he gets. Jonathan
participants (4)
-
Alex Chovanec
-
David Abrahams
-
Jonathan Turkanis
-
Thomas Witt