[mpl] bug: mpl::is_sequence< mpl::int_<1> > fails to compile

// testcase mpl bug, is_sequence [CVS HEAD, GCC, VC] #include <boost/mpl/assert.hpp> #include <boost/mpl/is_sequence.hpp> #include <boost/mpl/int.hpp> using namespace boost::mpl; BOOST_MPL_ASSERT_NOT(( is_sequence< int_<1> > )); -- Regards, Tobias

Ooops. I just noticed this problem has already been reported... For those about to reply "don't use is_sequence" please answer this question first: How to traverse a tree where the nodes are sequences and the leafes are mpl::intS without it? Tobias Schwinger wrote:
// testcase mpl bug, is_sequence [CVS HEAD, GCC, VC]
#include <boost/mpl/assert.hpp>
#include <boost/mpl/is_sequence.hpp> #include <boost/mpl/int.hpp>
using namespace boost::mpl;
BOOST_MPL_ASSERT_NOT(( is_sequence< int_<1> > ));
-- Regards,
Tobias
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Tobias Schwinger wrote:
Ooops. I just noticed this problem has already been reported...
OK, The problem is that e.g. "begin< int_<1> >" doesn't return "void_" (as stated in the documentation) but fails to compile instead. Here is a tentative patch that straightens this issue. The code in the modified file seems to assume that "sequence_tag" sorts out non-sequences -- so it /might/ be more elegant to fix the problem there. Anyway, there is no such guarantee (according to the documentation) and it would require another detection whether "begin_impl" has been specialized, so it might as well be worse. Regards, Tobias Index: boost/mpl/aux_/begin_end_impl.hpp =================================================================== RCS file: /cvsroot/boost/boost/boost/mpl/aux_/begin_end_impl.hpp,v retrieving revision 1.8 diff -u -r1.8 begin_end_impl.hpp --- boost/mpl/aux_/begin_end_impl.hpp 2 Sep 2004 15:40:43 -0000 1.8 +++ boost/mpl/aux_/begin_end_impl.hpp 7 May 2006 14:12:19 -0000 @@ -17,7 +17,9 @@ #include <boost/mpl/begin_end_fwd.hpp> #include <boost/mpl/sequence_tag_fwd.hpp> #include <boost/mpl/void.hpp> +#include <boost/mpl/eval_if.hpp> #include <boost/mpl/aux_/na.hpp> +#include <boost/mpl/aux_/has_begin.hpp> #include <boost/mpl/aux_/traits_lambda_spec.hpp> #include <boost/mpl/aux_/config/eti.hpp> @@ -27,12 +29,27 @@ // specializing either the 'begin_impl/end_impl' or the primary // 'begin/end' templates +namespace aux { + +template< typename Sequence > struct begin_type +{ + typedef typename Sequence::begin type; +}; + +template< typename Sequence > struct end_type +{ + typedef typename Sequence::end type; +}; + +} // namespace aux + template< typename Tag > struct begin_impl { template< typename Sequence > struct apply { - typedef typename Sequence::begin type; + typedef typename eval_if<aux::has_begin<Sequence,true_>, + aux::begin_type<Sequence>,void_ >::type type; }; }; @@ -41,7 +58,8 @@ { template< typename Sequence > struct apply { - typedef typename Sequence::end type; + typedef typename eval_if<aux::has_begin<Sequence,true_>, + aux::end_type<Sequence>,void_ >::type type; }; }; @@ -63,8 +81,7 @@ AUX778076_IMPL_SPEC(end, nested_begin_end_tag, typename Sequence::end) // if a type 'T' does not contain 'begin/end' or 'tag' members -// and doesn't specialize either 'begin/end' or 'begin_impl/end_impl' -// templates, then we end up here +// and doesn't specialize 'begin/end' templates, then we end up here AUX778076_IMPL_SPEC(begin, non_sequence_tag, void_) AUX778076_IMPL_SPEC(end, non_sequence_tag, void_) AUX778076_IMPL_SPEC(begin, na, void_)

Tobias Schwinger <tschwinger@neoscientists.org> writes:
For those about to reply "don't use is_sequence" please answer this question first:
How to traverse a tree where the nodes are sequences and the leafes are mpl::intS without it?
Just reformulate the tree so that nodes are all wrapped in something you can recognize reliably: tree< list< tree<vector<int, long> > , tree<vector<long,int> > > > -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
For those about to reply "don't use is_sequence" please answer this question first:
How to traverse a tree where the nodes are sequences and the leafes are mpl::intS without it?
Just reformulate the tree so that nodes are all wrapped in something you can recognize reliably:
tree< list< tree<vector<int, long> > , tree<vector<long,int> > > >
Pretty nice for hard-wired input -- impractical in my case, though. Why not make is_sequence work reliably? I understand the "concept detection hurts genericity" objection, but we shouldn't be too dogmatic about it -- that rule is not generic itself, after all. Regards, Tobias

Tobias Schwinger <tschwinger@neoscientists.org> writes:
David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
For those about to reply "don't use is_sequence" please answer this question first:
How to traverse a tree where the nodes are sequences and the leafes are mpl::intS without it?
Just reformulate the tree so that nodes are all wrapped in something you can recognize reliably:
tree< list< tree<vector<int, long> > , tree<vector<long,int> > > >
Pretty nice for hard-wired input -- impractical in my case, though. Why not make is_sequence work reliably?
Because it's impossible. It's always going to look for some features of the type being tested that can be present even if the type is not, in fact, a sequence. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
For those about to reply "don't use is_sequence" please answer this question first:
How to traverse a tree where the nodes are sequences and the leafes are mpl::intS without it?
Just reformulate the tree so that nodes are all wrapped in something you can recognize reliably:
tree< list< tree<vector<int, long> > , tree<vector<long,int> > > >
Pretty nice for hard-wired input -- impractical in my case, though. Why not make is_sequence work reliably?
Because it's impossible. It's always going to look for some features of the type being tested that can be present even if the type is not, in fact, a sequence.
I don't see why it is impossible. MPL sequences can always specialize is_sequence<T>. is_sequence *can* work reliably 100% of the time: template <class S> struct is_sequence : mpl::false_ {}; // default // your specializations here Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman <joel@boost-consulting.com> writes:
David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
Why not make is_sequence work reliably?
Because it's impossible. It's always going to look for some features of the type being tested that can be present even if the type is not, in fact, a sequence.
I don't see why it is impossible. MPL sequences can always specialize is_sequence<T>. is_sequence *can* work reliably 100% of the time:
template <class S> struct is_sequence : mpl::false_ {}; // default
// your specializations here
The problem isn't false negatives, it's false positives. In practice you can probably make it vanishingly unlikely that some random third-party type will pass all the default is_sequence tests, but you can never make it 100% impossible as long as: a. sequences can be arbitrary types, such as void(int,long,char) b. is_sequence<S> reporting correctly is not part of the sequence requirements -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Joel de Guzman <joel@boost-consulting.com> writes:
David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
Why not make is_sequence work reliably? Because it's impossible. It's always going to look for some features of the type being tested that can be present even if the type is not, in fact, a sequence. I don't see why it is impossible. MPL sequences can always specialize is_sequence<T>. is_sequence *can* work reliably 100% of the time:
template <class S> struct is_sequence : mpl::false_ {}; // default
// your specializations here
The problem isn't false negatives, it's false positives. In practice you can probably make it vanishingly unlikely that some random third-party type will pass all the default is_sequence tests, but you can never make it 100% impossible as long as:
a. sequences can be arbitrary types, such as void(int,long,char)
b. is_sequence<S> reporting correctly is not part of the sequence requirements
Is that a or b? or a and b? I believe that the default is_sequence should strictly be mpl::false_: no guessing games. I think: a) if you want to make specific types (e.g. RT(A0,A1,...AN)), a sequence, then specialize is_sequence for those. Sure you can do it for only a limited number of args, but, hey, so do other libraries that provide traits over function types/ member function pointer types, function pointers. Are there other arbitrary types you have in mind that can be sequences? If there are, specialize for them. Explicit is better than implicit, right? I think the guessing game where MPL is trying some heuristics as to how an MPL sequence looks like (e.g. it has a tag typedef, etc), is what's causing us problems. I think it is the wrong approach. b) If is_sequence is part of MPL's public interface, then it should give reliable results. If that requires b, then so be it. With Fusion, I had to go through hoops to get around this MPL problem. I really hope this issue is resolved once and for all. I believe it is possible for MPL to have a 100% correct is_sequence (no false positives/negatives). I believe that specialization is the correct approach, not some guessing game over how an MPL sequence might look like. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman <joel@boost-consulting.com> writes:
David Abrahams wrote:
Joel de Guzman <joel@boost-consulting.com> writes:
David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
Why not make is_sequence work reliably? Because it's impossible. It's always going to look for some features of the type being tested that can be present even if the type is not, in fact, a sequence. I don't see why it is impossible. MPL sequences can always specialize is_sequence<T>. is_sequence *can* work reliably 100% of the time:
template <class S> struct is_sequence : mpl::false_ {}; // default
// your specializations here
The problem isn't false negatives, it's false positives. In practice you can probably make it vanishingly unlikely that some random third-party type will pass all the default is_sequence tests, but you can never make it 100% impossible as long as:
a. sequences can be arbitrary types, such as void(int,long,char)
b. is_sequence<S> reporting correctly is not part of the sequence requirements
Is that a or b? or a and b?
I think it's OR.
I believe that the default is_sequence should strictly be mpl::false_: no guessing games.
I think:
a) if you want to make specific types (e.g. RT(A0,A1,...AN)), a sequence, then specialize is_sequence for those.
ODR violations ensue. int(int,long) is a sequence in one TU but not in another.
Sure you can do it for only a limited number of args, but, hey, so do other libraries that provide traits over function types/ member function pointer types, function pointers. Are there other arbitrary types you have in mind that can be sequences? If there are, specialize for them. Explicit is better than implicit, right? I think the guessing game where MPL is trying some heuristics as to how an MPL sequence looks like (e.g. it has a tag typedef, etc), is what's causing us problems. I think it is the wrong approach.
b) If is_sequence is part of MPL's public interface, then it should give reliable results. If that requires b, then so be it.
Right. Which is why I encouraged Aleksey to remove it. I'm surprised it's even documented in the reference manual. -- Dave Abrahams Boost Consulting www.boost-consulting.com

I went through the code just yesterday, so... David Abrahams wrote:
Joel de Guzman <joel@boost-consulting.com> writes:
David Abrahams wrote:
Joel de Guzman <joel@boost-consulting.com> writes:
David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
Why not make is_sequence work reliably?
Because it's impossible. It's always going to look for some features of the type being tested that can be present even if the type is not, in fact, a sequence.
The guessing stuff directly in 'is_sequence' is a workaround for broken compilers. For proper compilers: is_sequence<T> <=> is_same< begin<T>, void_ > . So 'is_sequence' works perfectly well and can also work perfectly reliable given that 'begin' does; 'begin', however, fails to compile if there is a 'tag' member for a non-sequence type instead of returning 'void_' which would be the documented behaviour. The reason is "guessing mess" in 'begin_impl': the primary template attempts to return 'T::begin' (it seems to be a convenience feature so a sequence implementor can simply specify a type member called 'begin' and does not need to specialize 'begin_impl'). 'sequence_tag' is assumed to sort out non-sequences -- but it is impossible without guessing (or adding to the user's responsibilities when registering non-sequence types for tag dispatch).
I don't see why it is impossible. MPL sequences can always specialize is_sequence<T>. is_sequence *can* work reliably 100% of the time:
template <class S> struct is_sequence : mpl::false_ {}; // default
// your specializations here
That should not even be needed -- implementing 'begin' should be enough...
The problem isn't false negatives, it's false positives. In practice you can probably make it vanishingly unlikely that some random third-party type will pass all the default is_sequence tests, but you can never make it 100% impossible as long as:
a. sequences can be arbitrary types, such as void(int,long,char)
b. is_sequence<S> reporting correctly is not part of the sequence requirements
c. begin works properly 'begin<non_sequence>::type' being 'void_' (maybe more importantly that it compiles) is already part of the requirements...
I believe that the default is_sequence should strictly be mpl::false_: no guessing games.
I think:
a) if you want to make specific types (e.g. RT(A0,A1,...AN)), a sequence, then specialize is_sequence for those.
ODR violations ensue. int(int,long) is a sequence in one TU but not in another.
Just curious: all these classes should have internal linkage - am I missing something?
Sure you can do it for only a limited number of args, but, hey, so do other libraries that provide traits over function types/ member function pointer types, function pointers. Are there other arbitrary types you have in mind that can be sequences? If there are, specialize for them. Explicit is better than implicit, right? I think the guessing game where MPL is trying some heuristics as to how an MPL sequence looks like (e.g. it has a tag typedef, etc), is what's causing us problems. I think it is the wrong approach.
b) If is_sequence is part of MPL's public interface, then it should give reliable results. If that requires b, then so be it.
Right. Which is why I encouraged Aleksey to remove it. I'm surprised it's even documented in the reference manual.
The dispatcher for sequence operations won't become any better from removing 'is_sequence' (surprise guaranteed ;P )... So here's my €0,02-plan: -- require 'begin_impl' to be specialized; IOW drop that "type member convenience stuff" (described above) -- it is what makes the dispatch a mess! -- throw out 'sequence_tag' (it doesn't work, can't work and isn't needed) -- keep 'is_sequence' ;-) Regards, Tobias

Tobias Schwinger <tschwinger@neoscientists.org> writes:
I went through the code just yesterday, so...
The guessing stuff directly in 'is_sequence' is a workaround for
broken compilers. For proper compilers:
is_sequence<T> <=> is_same< begin<T>, void_ >
. So 'is_sequence' works perfectly well and can also work perfectly
reliable given that 'begin' does; 'begin', however, fails to compile
if there is a 'tag' member for a non-sequence type instead of
returning 'void_' which would be the documented behaviour.
And begin can be spoofed, too, can't it? Okay, it's very unlikely.
The reason is "guessing mess" in 'begin_impl': the primary template
attempts to return 'T::begin' (it seems to be a convenience feature so
a sequence implementor can simply specify a type member called 'begin'
and does not need to specialize 'begin_impl'). 'sequence_tag' is
assumed to sort out non-sequences -- but it is impossible without
guessing (or adding to the user's responsibilities when registering
non-sequence types for tag dispatch).
Exactly.
I don't see why it is impossible. MPL sequences can always
specialize is_sequence<T>. is_sequence *can* work reliably
100% of the time:
template <class S> struct is_sequence : mpl::false_ {}; //
default
// your specializations here
That should not even be needed -- implementing 'begin' should be
enough...
I am not thinking about this problem freshly today; all I remember is that it's a morasse with no good answers. But I could be wrong. If you can supply a patch that works, and you can demonstrate it with convincing tests, I'll be happy to recommend that Aleksey commit it.
The problem isn't false negatives, it's false positives. In
practice
you can probably make it vanishingly unlikely that some random
third-party type will pass all the default is_sequence tests, but
you
can never make it 100% impossible as long as:
a. sequences can be arbitrary types, such as void(int,long,char)
b. is_sequence<S> reporting correctly is not part of the sequence
requirements
c. begin works properly
'begin<non_sequence>::type' being 'void_' (maybe more importantly that
it compiles) is already part of the requirements...
Yes but it *totally* begs the question. Because, what do you have to know in order to decide whether begin<x> should return void_? That's right, you have to know whether x is a sequence.
I believe that the default is_sequence should strictly be
mpl::false_: no guessing games.
I think:
a) if you want to make specific types (e.g. RT(A0,A1,...AN)),
a sequence, then specialize is_sequence for those.
ODR violations ensue. int(int,long) is a sequence in one TU but not
in another.
Just curious: all these classes should have internal linkage - am I
missing something?
foo<is_sequence<int(int,long)>::value>::bar() calls one function in TU #1 and another function in TU #2. Clear yet?
So here's my €0,02-plan:
-- require 'begin_impl' to be specialized; IOW drop that "type member
convenience stuff" (described above) -- it is what makes the dispatch
a mess! -- throw out 'sequence_tag' (it doesn't work, can't work and
isn't needed) -- keep 'is_sequence' ;-)
Show me the money. Err, the code. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
The guessing stuff directly in 'is_sequence' is a workaround for broken compilers. For proper compilers:
is_sequence<T> <=> is_same< begin<T>, void_ >
Was actually supposed to mean 'is_same<begin<T>::type,void_>' or just assume a placeholder expression around it...
. So 'is_sequence' works perfectly well and can also work perfectly reliable given that 'begin' does; 'begin', however, fails to compile if there is a 'tag' member for a non-sequence type instead of returning 'void_' which would be the documented behaviour.
And begin can be spoofed, too, can't it? Okay, it's very unlikely.
Depends. If that guessing T::begin shortcut is gone it can't -- unless you call implementing an incomplete sequence (specializing 'begin_impl') spoofing.
The reason is "guessing mess" in 'begin_impl': the primary template attempts to return 'T::begin' (it seems to be a convenience feature so a sequence implementor can simply specify a type member called 'begin' and does not need to specialize 'begin_impl'). 'sequence_tag' is assumed to sort out non-sequences -- but it is impossible without guessing (or adding to the user's responsibilities when registering non-sequence types for tag dispatch).
Exactly.
I don't see why it is impossible. MPL sequences can always specialize is_sequence<T>. is_sequence *can* work reliably 100% of the time: template <class S> struct is_sequence : mpl::false_ {}; // default // your specializations here
That should not even be needed -- implementing 'begin' should be enough...
I am not thinking about this problem freshly today; all I remember is that it's a morasse with no good answers. But I could be wrong.
If you can supply a patch that works, and you can demonstrate it with convincing tests, I'll be happy to recommend that Aleksey commit it.
Well, if that's all you're worried about: I supplied a patch that works and a testcase in the OP (and the assertions from the documentation pass, too). I'm currently using Fusion on top of it and I didn't encounter any problems. It shouldn't break things more than they're already broken, still it only mutes the symptoms of the underlying problem. A proper solution (e.g. as proposed at the end of my previous post) means significant changes to the library so that I wouldn't want to start coding until you (plural, authors of MPL) agree that the proposed changes are desirable.
you can probably make it vanishingly unlikely that some random third-party type will pass all the default is_sequence tests, but you can never make it 100% impossible as long as:
a. sequences can be arbitrary types, such as void(int,long,char) b. is_sequence<S> reporting correctly is not part of the sequence requirements
c. begin works properly
'begin<non_sequence>::type' being 'void_' (maybe more importantly that it compiles) is already part of the requirements...
Yes but it *totally* begs the question. Because, what do you have to know in order to decide whether begin<x> should return void_? That's right, you have to know whether x is a sequence.
Simple: You gotta implement 'begin' when implementing a sequence. If it's not implemented it's not a sequence. The requirement to return 'void_' for non-sequences forbids "overloading" that metafunction for non-sequence types.
I believe that the default is_sequence should strictly be mpl::false_: no guessing games. I think: a) if you want to make specific types (e.g. RT(A0,A1,...AN)), a sequence, then specialize is_sequence for those.
ODR violations ensue. int(int,long) is a sequence in one TU but not in another.
Just curious: all these classes should have internal linkage - am I missing something?
foo<is_sequence<int(int,long)>::value>::bar() calls one function in TU #1 and another function in TU #2.
True. foo<true> and foo<false> are two different classes...
Clear yet?
Sorry, not quite. Where is the redefinition?
So here's my €0,02-plan: -- require 'begin_impl' to be specialized; IOW drop that "type member convenience stuff" (described above) -- it is what makes the dispatch a mess! -- throw out 'sequence_tag' (it doesn't work, can't work and isn't needed) -- keep 'is_sequence' ;-)
Show me the money. Err, the code.
Please think and comment on it first (plural again, includes Aleksey). The patch I posted works for me, right now. Getting the underlying flaw straightened out is primarily *your* business (I'll probably implement it iff there is a clear demand). Regards, Tobias

Tobias Schwinger <tschwinger@neoscientists.org> writes:
foo<is_sequence<int(int,long)>::value>::bar() calls one function in TU #1 and another function in TU #2.
True. foo<true> and foo<false> are two different classes...
Clear yet?
Sorry, not quite. Where is the redefinition?
Look up the definition of ODR in the standard and you will see that this qualifies. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Tobias Schwinger <tschwinger@neoscientists.org> writes:
foo<is_sequence<int(int,long)>::value>::bar() calls one function in TU #1 and another function in TU #2.
True. foo<true> and foo<false> are two different classes...
Clear yet?
Sorry, not quite. Where is the redefinition?
Look up the definition of ODR in the standard and you will see that this qualifies.
Right, it worked [ 3.2-5-2 ]. Thanks for the hints! But is it a good idea to have a builtin type model a sequence in the first place? I don't think so, because there can be a PoI before the specialization is defined! It's just as error prone without 'is_sequence'... Regards, Tobias
participants (3)
-
David Abrahams
-
Joel de Guzman
-
Tobias Schwinger