[mpl] as_sequence< X< Y > > instantiates typedefs in X< Y >

Hi there I'm trying to address a point raised by Jonathan Turkanis during the formal review of the statechart library. Basically, the problem boils down to the following: #include <boost/mpl/list.hpp> #include <boost/mpl/size.hpp> #include <boost/mpl/as_sequence.hpp> namespace mpl = boost::mpl; // InnerInitial is either an mpl sequence of types or a single type template< class InnerInitial > struct state { // stateArray_ must have a length equal to the number of types // in InnerInitial int stateArray_[ mpl::size< typename mpl::as_sequence< InnerInitial >::type >::type::value ]; }; // A is defined later, possibly in a different translation unit struct A; template< class X > struct B { typedef typename X::reactions reactions; }; int main() { // fine: instantiation with an incomplete type state< A > a; // fine: instantiation with an mpl sequence containing B< A > state< mpl::list< B< A > > > b; // error: mpl::as_sequence triggers the instantiation of B's member // typedef, which tries to access the member typedef of A. A compile- // time error results because A is still incomplete at this point. state< B< A > > c; // *** here *** return 0; } MSVC7.1 and GCC3.4 agree that the last state template instantiation is illegal (use of undefined type A). I'm wondering whether there is any way to make this legal (different as_sequence implementation, implement the state template differently, etc.)? Thanks & Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

"Andreas Huber" <ahd6974-spamgroupstrap@yahoo.com> writes:
// InnerInitial is either an mpl sequence of types or a single type template< class InnerInitial > struct state
Interfaces like this one are generally a bad idea because they kill genericity and create surprising behaviors in the degenerate case where you have a single type that happens to be a sequence. http://lists.boost.org/MailArchives/boost/msg55143.php http://lists.boost.org/MailArchives/boost/msg55098.php -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
"Andreas Huber" <ahd6974-spamgroupstrap@yahoo.com> writes:
// InnerInitial is either an mpl sequence of types or a single type template< class InnerInitial > struct state
Interfaces like this one are generally a bad idea because they kill genericity and create surprising behaviors in the degenerate case where you have a single type that happens to be a sequence.
Ok, I guess the example was bit too removed from reality. I should have said that the types passed as InnerInitial need to be models of concepts. InnerInitial requirements from the statechart docs: <quote> An mpl::list<> containing models of the SimpleState or State concepts or instantiations of the shallow_history or deep_history class templates. If there is only a single inner initial state that is not a template instantiation then it can also be passed directly, without wrapping it into an mpl::list<>. </quote> The requirements for SimpleState and State are such that they cannot be mpl sequences... Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

"Andreas Huber" <ahd6974-spamgroupstrap@yahoo.com> writes:
<quote> An mpl::list<> containing models of the SimpleState or State concepts or instantiations of the shallow_history or deep_history class templates. If there is only a single inner initial state that is not a template instantiation then it can also be passed directly, without wrapping it into an mpl::list<>. </quote>
Why are you requiring that the user pass mpl::list<> as opposed to some other mpl sequence? -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote: [snip]
Why are you requiring that the user pass mpl::list<> as opposed to some other mpl sequence?
I need to modify that sequence internally. If any sequence was allowed (including immutable ones), I would need to copy that sequence into an mpl::list<> first. I suspect that the copying slows down compilation noticeably (I never had the chance to make measurements, so this may well be FUD), so I figured that requiring a list is the best way to give the user the opportunity to keep compilation as fast as possible. Sure, I could make a copy only if the user doesn't pass an mpl::list<> and document that in the interface but nobody has ever made a request in this direction. I guess this is due to the fact that pretty much everybody specifies the InnerInitial states inline: struct Y; struct Z; struct X : sc::simple_state< X, Machine, mpl::list< Y, Z > > {}; That is, I can't really think of a real-world example where you would want to use an algorithm to fill that list... Would you allow any mpl sequence anyway? Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

"Andreas Huber" <ahd6974-spamgroupstrap@yahoo.com> writes:
David Abrahams wrote: [snip]
Why are you requiring that the user pass mpl::list<> as opposed to some other mpl sequence?
I need to modify that sequence internally.
You don't mean that, exactly, since all metadata is immutable.
If any sequence was allowed (including immutable ones), I would need to copy that sequence into an mpl::list<> first.
Why an mpl::list<>? What's special about that structurue? Why not mpl::vector? Are you sure you couldn't get away with using some views onto the original sequence?
I suspect that the copying slows down compilation noticeably (I never had the chance to make measurements, so this may well be FUD), so I figured that requiring a list is the best way to give the user the opportunity to keep compilation as fast as possible.
It's not; a vector would be faster. Or you could just require any front extensible sequence, in which case, maybe deque is what you need?
Sure, I could make a copy only if the user doesn't pass an mpl::list<> and document that in the interface but nobody has ever made a request in this direction. I guess this is due to the fact that pretty much everybody specifies the InnerInitial states inline:
struct Y; struct Z; struct X : sc::simple_state< X, Machine, mpl::list< Y, Z > > {};
Why mpl::list and not mpl::vector or mpl::deque?
That is, I can't really think of a real-world example where you would want to use an algorithm to fill that list...
Would you allow any mpl sequence anyway?
I certainly wouldn't require a list. Whether to accept every possible sequence is another question. I'm sure there are many models of the concept you actually need from the user. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes: [snip]
I need to modify that sequence internally.
You don't mean that, exactly, since all metadata is immutable.
Right, I mean that I can't use e.g. push_front on some sequences.
If any sequence was allowed (including immutable ones), I would need to copy that sequence into an mpl::list<> first.
Why an mpl::list<>? What's special about that structurue? Why not mpl::vector?
The sequence just needs to support certain operations, e.g. push_front, reverse, etc. So, mpl::vector<> should work too (I never checked).
Are you sure you couldn't get away with using some views onto the original sequence?
I'm not. Views are something I probably need to try when I get the time to make compilation time optimizations.
I suspect that the copying slows down compilation noticeably (I never had the chance to make measurements, so this may well be FUD), so I figured that requiring a list is the best way to give the user the opportunity to keep compilation as fast as possible.
It's not; a vector would be faster.
Ok, that's good to know.
Or you could just require any front extensible sequence, in which case, maybe deque is what you need?
Don't know, I can't think of anything that would keep you from using deque or vector. I guess it would be best to document the exact requirements and make measurements so that I can tell the user which of the standard mpl sequences are best.
Sure, I could make a copy only if the user doesn't pass an mpl::list<> and document that in the interface but nobody has ever made a request in this direction. I guess this is due to the fact that pretty much everybody specifies the InnerInitial states inline:
struct Y; struct Z; struct X : sc::simple_state< X, Machine, mpl::list< Y, Z > > {};
Why mpl::list and not mpl::vector or mpl::deque?
See above. All of them should work.
That is, I can't really think of a real-world example where you would want to use an algorithm to fill that list...
Would you allow any mpl sequence anyway?
I certainly wouldn't require a list. Whether to accept every possible sequence is another question. I'm sure there are many models of the concept you actually need from the user.
Agreed, an appropriate item is now on my to-do list. Thanks & Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

Andreas Huber <ahd6974-spamgroupstrap@yahoo.com> writes:
David Abrahams <dave <at> boost-consulting.com> writes: [snip]
I need to modify that sequence internally.
You don't mean that, exactly, since all metadata is immutable.
Right, I mean that I can't use e.g. push_front on some sequences.
If any sequence was allowed (including immutable ones), I would need to copy that sequence into an mpl::list<> first.
Why an mpl::list<>? What's special about that structurue? Why not mpl::vector?
The sequence just needs to support certain operations, e.g. push_front, reverse, etc. So, mpl::vector<> should work too (I never checked).
If you're reversing the sequence, all your arguments about requiring mutability for efficiency reasons are invalid. You might as well do mpl::reverse<user_sequence, back_inserter<vector<> > >
Don't know, I can't think of anything that would keep you from using deque or vector. I guess it would be best to document the exact requirements and make measurements so that I can tell the user which of the standard mpl sequences are best.
Not a bad idea, but I wouldn't go overboard. In this case the first priority should be to lift needless restrictions. The restrictions you're using don't even neccessarily lead to better efficiency. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
Why an mpl::list<>? What's special about that structurue? Why not mpl::vector?
The sequence just needs to support certain operations, e.g. push_front, reverse, etc. So, mpl::vector<> should work too (I never checked).
If you're reversing the sequence, all your arguments about requiring mutability for efficiency reasons are invalid. You might as well do
I don't think so, I'm only reversing the sequence *after* performing other mutating operations on it. Here's an example from the current implementation (context_type_list is assembled from multiple InnerInitial lists): template< class CommonContext, class DestinationState > struct make_context_list { typedef typename mpl::reverse< typename mpl::push_front< typename mpl::erase< typename DestinationState::context_type_list, typename mpl::find< typename DestinationState::context_type_list, CommonContext >::type, typename mpl::end< typename DestinationState::context_type_list >::type >::type, DestinationState
::type >::type type; };
[I know that this code needs improvement]
Don't know, I can't think of anything that would keep you from using deque or vector. I guess it would be best to document the exact requirements and make measurements so that I can tell the user which of the standard mpl sequences are best.
Not a bad idea, but I wouldn't go overboard. In this case the first priority should be to lift needless restrictions.
I've actually already added two to-do items. The one dealing with the restrictions appears before the measurements.
The restrictions you're using don't even neccessarily lead to better efficiency.
Right, I got that :) Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

Andreas Huber <ahd6974-spamgroupstrap <at> yahoo.com> writes:
I don't think so, I'm only reversing the sequence *after* performing other mutating operations on it. Here's an example from the current implementation (context_type_list is assembled from multiple InnerInitial lists):
I just had a look at the code and it turns out that the above is wrong (context_type_list is filled form scratch and not by modifying user-supplied sequences). The only operations that the InnerInitial sequence must support are: - size - empty - at - at_c - begin - end - erase Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

Andreas Huber <ahd6974-spamgroupstrap@yahoo.com> writes:
Andreas Huber <ahd6974-spamgroupstrap <at> yahoo.com> writes:
I don't think so, I'm only reversing the sequence *after* performing other mutating operations on it. Here's an example from the current implementation (context_type_list is assembled from multiple InnerInitial lists):
I just had a look at the code and it turns out that the above is wrong (context_type_list is filled form scratch and not by modifying user-supplied sequences). The only operations that the InnerInitial sequence must support are: - size - empty - at
?? list<> doesn't support that!
- at_c - begin - end - erase
-- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes:
- size - empty - at
?? list<> doesn't support that!
You mean at? I've had another look an I am positive that it must support it somehow. I'm not including any other containers than mpl::list! At least for 1.32. I haven't tried with 1.33 yet. Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.

Andreas Huber <ahd6974-spamgroupstrap@yahoo.com> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
- size - empty - at
?? list<> doesn't support that!
You mean at? I've had another look an I am positive that it must support it somehow. I'm not including any other containers than mpl::list! At least for 1.32. I haven't tried with 1.33 yet.
Well, if list<> supports at<> it's an extension. It certainly isn't O(1). mpl::list is not a random access sequence. Ah, I see that at<> is using advance<>, which is why it "works." I wonder if we actually ought to be causing a compilation error in this case. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Andreas Huber <ahd6974-spamgroupstrap@yahoo.com> writes:
David Abrahams <dave <at> boost-consulting.com> writes:
If you're reversing the sequence, all your arguments about requiring mutability for efficiency reasons are invalid. You might as well do
I don't think so, I'm only reversing the sequence *after* performing other mutating operations on it. Here's an example from the current implementation (context_type_list is assembled from multiple InnerInitial lists):
template< class CommonContext, class DestinationState > struct make_context_list { typedef typename mpl::reverse< typename mpl::push_front< typename mpl::erase< typename DestinationState::context_type_list, typename mpl::find< typename DestinationState::context_type_list, CommonContext >::type, typename mpl::end< typename DestinationState::context_type_list >::type >::type, DestinationState
::type >::type type; };
[I know that this code needs improvement]
If you're erasing items at the end of a list<>, that's *very* expensive! So, sorry to be blunt, but I maintain your arguments about efficiency are invalid. Not to mention that they're not backed up by measurements. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams <dave <at> boost-consulting.com> writes: [snip code]
[I know that this code needs improvement]
If you're erasing items at the end of a list<>, that's *very* expensive!
Yep, I know that, that's why I wrote that the code needs improvement. Plus, as I said, my original argument regarding this code doesn't hold water anyway...
So, sorry to be blunt, but I maintain your arguments about efficiency are invalid.
I agree that mpl::list is a poor choice. BTW, I faintly remember having tried vector right in the beginning (~2.5 years ago) but abandoned it because it didn't work very well with MSVC7.0. So I guess I'm currently requiring mpl::list just for historical reasons... Regards, -- Andreas Huber When replying by private email, please remove the words spam and trap from the address shown in the header.
participants (2)
-
Andreas Huber
-
David Abrahams