In my continuing effort to demonstrate my inability to get much of anything working with the MPL, I'm having trouble inserting a type into an existing set of types. The code compiles and runs, but the revised type sequence is empty. Here's the code: #include <boost/mpl/set.hpp> #include <boost/mpl/for_each.hpp> #include <typeinfo> #include <iostream> namespace mpl = boost::mpl; struct Base { virtual ~Base(){} }; struct A { virtual ~A(){} }; struct B { virtual ~B(){} }; struct Print { template<typename T> void operator()(const T&) { std::cout << typeid(T).name() << ", "; } }; int main() { typedef mpl::set<A, B> Constraints; // print contents of Constraints (should be the equivalent // of "[A, B,]") std::cout << '['; mpl::for_each<Constraints>(Print()); std::cout << "]\n"; // add Base to Constraints, call result BaseClasses typedef mpl::insert<Constraints, mpl::end<Constraints>, Base> BaseClasses; // print contents of BaseClasses (should be the equivalent // of "[A, B, Base, ]") std::cout << '['; mpl::for_each<BaseClasses>(Print()); std::cout << "]\n"; } Can anybody spot what I'm doing wrong? I know that I could use the two-argument form of insert with a set, but I'm trying to write code that will work with a vector, too, and at any rate, I get the same results with the two-argument form of set. Thanks, Scott
I think the problem is in the typedef statement below: typedef mpl::insert<Constraints, mpl::end<Constraints>, Base> BaseClasses; mpl::insert is an own type but you need the new sequence type with is accessed via: mpl::insert<...>::type Best Regards, Ovanes -----Original Message----- From: Scott Meyers [mailto:usenet@aristeia.com] Sent: Donnerstag, 29. März 2007 00:28 To: boost-users@lists.boost.org Subject: [Boost-users] [mpl] insert not inserting? In my continuing effort to demonstrate my inability to get much of anything working with the MPL, I'm having trouble inserting a type into an existing set of types. The code compiles and runs, but the revised type sequence is empty. Here's the code: #include <boost/mpl/set.hpp> #include <boost/mpl/for_each.hpp> #include <typeinfo> #include <iostream> namespace mpl = boost::mpl; struct Base { virtual ~Base(){} }; struct A { virtual ~A(){} }; struct B { virtual ~B(){} }; struct Print { template<typename T> void operator()(const T&) { std::cout << typeid(T).name() << ", "; } }; int main() { typedef mpl::set<A, B> Constraints; // print contents of Constraints (should be the equivalent // of "[A, B,]") std::cout << '['; mpl::for_each<Constraints>(Print()); std::cout << "]\n"; // add Base to Constraints, call result BaseClasses typedef mpl::insert<Constraints, mpl::end<Constraints>, Base> BaseClasses; // print contents of BaseClasses (should be the equivalent // of "[A, B, Base, ]") std::cout << '['; mpl::for_each<BaseClasses>(Print()); std::cout << "]\n"; } Can anybody spot what I'm doing wrong? I know that I could use the two-argument form of insert with a set, but I'm trying to write code that will work with a vector, too, and at any rate, I get the same results with the two-argument form of set. Thanks, Scott _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
I have one more suggestion, how to deal with such problems. If the code compiles but does not do what you want it is somewhat difficult to understand where the error is. I find the debugging ablity of MSVC extremely useful. Even using the free edition one can instanciate the produced type and see in the watch window of which type the instantiated object is. In this case it woule be obvious that the type is mpl::insert<Constraints, mpl::end<Constraints>, Base> and not the type to which compile time insert<...>::type member would expand. I understand there are other tools and possibilities like static assertions etc. which do the trick, but this one is visual... Best Regards, Ovanes P.S. would be great to have smth. like a C++ Metaprogramm Debugger ;) -----Original Message----- From: Ovanes Markarian [mailto:om_boost@keywallet.com] Sent: Mittwoch, 28. März 2007 23:34 To: boost-users@lists.boost.org Subject: Re: [Boost-users] [mpl] insert not inserting? I think the problem is in the typedef statement below: typedef mpl::insert<Constraints, mpl::end<Constraints>, Base> BaseClasses; mpl::insert is an own type but you need the new sequence type with is accessed via: mpl::insert<...>::type Best Regards, Ovanes -----Original Message----- From: Scott Meyers [mailto:usenet@aristeia.com] Sent: Donnerstag, 29. März 2007 00:28 To: boost-users@lists.boost.org Subject: [Boost-users] [mpl] insert not inserting? In my continuing effort to demonstrate my inability to get much of anything working with the MPL, I'm having trouble inserting a type into an existing set of types. The code compiles and runs, but the revised type sequence is empty. Here's the code: #include <boost/mpl/set.hpp> #include <boost/mpl/for_each.hpp> #include <typeinfo> #include <iostream> namespace mpl = boost::mpl; struct Base { virtual ~Base(){} }; struct A { virtual ~A(){} }; struct B { virtual ~B(){} }; struct Print { template<typename T> void operator()(const T&) { std::cout << typeid(T).name() << ", "; } }; int main() { typedef mpl::set<A, B> Constraints; // print contents of Constraints (should be the equivalent // of "[A, B,]") std::cout << '['; mpl::for_each<Constraints>(Print()); std::cout << "]\n"; // add Base to Constraints, call result BaseClasses typedef mpl::insert<Constraints, mpl::end<Constraints>, Base> BaseClasses; // print contents of BaseClasses (should be the equivalent // of "[A, B, Base, ]") std::cout << '['; mpl::for_each<BaseClasses>(Print()); std::cout << "]\n"; } Can anybody spot what I'm doing wrong? I know that I could use the two-argument form of insert with a set, but I'm trying to write code that will work with a vector, too, and at any rate, I get the same results with the two-argument form of set. Thanks, Scott _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Ovanes Markarian wrote:
mpl::insert is an own type but you need the new sequence type with is accessed via: mpl::insert<...>::type
Thanks very much. When I change the corresponding to code to: typedef mpl::insert<Constraints, mpl::end<Constraints>, Base>::type BaseClasses; then Base is inserted, but the resulting sequence has only A and Base in it, not A and B and Base, i.e., the type B is somehow being lost. Any idea why? Also, I'll be honest: I don't know when to use ::type and when not to. Is there a good description somewhere (either online or in the MPL book) that will clarify the situation for me? Thanks, Scott
----- Mensaje original ----- De: Scott Meyers <usenet@aristeia.com> Fecha: Miércoles, Marzo 28, 2007 11:49 pm Asunto: Re: [Boost-users] [mpl] insert not inserting? Para: boost-users@lists.boost.org
Ovanes Markarian wrote:
mpl::insert is an own type but you need the new sequence type with is accessed via: mpl::insert<...>::type
Thanks very much. When I change the corresponding to code to:
typedef mpl::insert<Constraints, mpl::end<Constraints>, Base>::type BaseClasses;
then Base is inserted, but the resulting sequence has only A and Base in it, not A and B and Base, i.e., the type B is somehow being lost. Any idea why?
I think you want to write mpl::end<Constraints>::type
Also, I'll be honest: I don't know when to use ::type and when not to. Is there a good description somewhere (either online or in the MPL book) that will clarify the situation for me?
Well, excuse me if the following is obvious to you, but ::type is to a metafunction what actual invocation is to a run-time function. So, mpl::end<Constraints> refers to the name of the entity, but does not actually compute its "return value" unless you add the ::type suffix. Sort of like the difference between &foo and foo() in the run-time world. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
Just as addition to the issue: here you deal with a set. And in the Extensible Associative Sequence the position is ignored (at least according to the docs). So you can skip the usage of end<Constraints>. * insert<Sequence,Pos,T> performs an insertion of type T at an arbitrary position Pos in Sequence. Pos is ignored if Sequence is a model of Extensible Associative Sequence. * insert<Sequence,T> is a shortcut notation for insert<Sequence,Pos,T> for the case when Sequence is a model of Extensible Associative Sequence. MSVC 8 compiles it in both cases with and without end< set<...> > and with end< set<...> >::type to boost::mpl::s_item<C,boost::mpl::set<A,B,boost::mpl::na,boost::mpl::na,boost ::mpl::na,boost::mpl::na,boost::mpl::na,boost::mpl::na,boost::mpl::na,boost: :mpl::na,boost::mpl::na,boost::mpl::na,boost::mpl::na,boost::mpl::na,boost:: mpl::na,boost::mpl::na,boost::mpl::na,boost::mpl::na,boost::mpl::na,boost::m pl::na> > Best Regards, Ovanes -----Original Message----- From: "JOAQUIN LOPEZ MU?Z" [mailto:joaquin@tid.es] Sent: Donnerstag, 29. März 2007 00:08 To: boost-users@lists.boost.org Subject: Re: [Boost-users] [mpl] insert not inserting? ----- Mensaje original ----- De: Scott Meyers <usenet@aristeia.com> Fecha: Miércoles, Marzo 28, 2007 11:49 pm Asunto: Re: [Boost-users] [mpl] insert not inserting? Para: boost-users@lists.boost.org
Ovanes Markarian wrote:
mpl::insert is an own type but you need the new sequence type with is accessed via: mpl::insert<...>::type
Thanks very much. When I change the corresponding to code to:
typedef mpl::insert<Constraints, mpl::end<Constraints>, Base>::type BaseClasses;
then Base is inserted, but the resulting sequence has only A and Base in it, not A and B and Base, i.e., the type B is somehow being lost. Any idea why?
I think you want to write mpl::end<Constraints>::type
Also, I'll be honest: I don't know when to use ::type and when not to. Is there a good description somewhere (either online or in the MPL book) that will clarify the situation for me?
Well, excuse me if the following is obvious to you, but ::type is to a metafunction what actual invocation is to a run-time function. So, mpl::end<Constraints> refers to the name of the entity, but does not actually compute its "return value" unless you add the ::type suffix. Sort of like the difference between &foo and foo() in the run-time world. Joaquín M López Muñoz Telefónica, Investigación y Desarrollo _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
Ovanes Markarian wrote:
Just as addition to the issue: here you deal with a set. And in the Extensible Associative Sequence the position is ignored (at least according to the docs). So you can skip the usage of end<Constraints>.
Right, but as I noted in my original post, (1) using the two-argument form for insert does not change the observed behavior, and (2) I'd prefer to use the form taking a position, anyway, in case I decide to start using a container like mpl:: vector. At this point, however, I'd be happy to use any form that actually results in a new sequence that has one more element than the original sequence. Scott
Scott Meyers wrote:
Ovanes Markarian wrote:
Just as addition to the issue: here you deal with a set. And in the Extensible Associative Sequence the position is ignored (at least according to the docs). So you can skip the usage of end<Constraints>.
Right, but as I noted in my original post, (1) using the two-argument form for insert does not change the observed behavior, and (2) I'd prefer to use the form taking a position, anyway, in case I decide to start using a container like mpl:: vector.
At this point, however, I'd be happy to use any form that actually results in a new sequence that has one more element than the original sequence.
mpl::vector and mpl::push_back (instead of insert) seem to work.
Scott
Regards, dave
David Klein wrote:
mpl::vector and mpl::push_back (instead of insert) seem to work.
mpl::vector and insert seems to work, too. Thanks for the workaround. Now if somebody can just show me how to make this work with mpl::set, my life will be complete. Or, you know, at least better :-) Thanks, Scott
JOAQUIN LOPEZ MU?Z wrote:
I think you want to write
mpl::end<Constraints>::type
Thanks for the suggestion, but it doesn't change the behavior of the program: the result of the insertion of a new element into the two-element set is a new set with only two elements (one of the original elements is lost). Please try it and see if you get the same thing I do.
Well, excuse me if the following is obvious to you, but ::type is to a metafunction what actual invocation is to a run-time function. So, mpl::end<Constraints> refers to the name of the entity, but does not actually compute its "return value" unless you add the ::type suffix.
I don't think this always holds. For example, I don't need to add the ::type suffix when I make a typedef or when I do an assertion: typedef mpl::set<A, B> MySet; BOOST_MPL_ASSERT(( mpl::equal<mpl::set<A,B>, mpl::set<A,B> > )); Until this thread, I figured I could wallow in ignorance about when ::type was needed and when not, because I assumed that if I got it wrong, the code would not compile. (Sort of the way a lot of people think about const...) Now I know better, sigh. Scott
on Wed Mar 28 2007, Scott Meyers <usenet-AT-aristeia.com> wrote:
JOAQUIN LOPEZ MU?Z wrote:
I think you want to write
mpl::end<Constraints>::type
Thanks for the suggestion, but it doesn't change the behavior of the program: the result of the insertion of a new element into the two-element set is a new set with only two elements (one of the original elements is lost). Please try it and see if you get the same thing I do.
Scott, I hate to say this, but I really think your first problem demonstrated that mpl::set is too broken to use. I've asked Aleksey to look at it, but he's not answering (maybe on vacation, I dunno). I'd like to fix it myself as I was the one who came up with the basic mechanisms for mpl::set, but unfortunately I can't tell what Aleksey intended in his realization of those ideas so I'm not quite sure where to start. You might see if you can use mpl::map to do the same things.
Well, excuse me if the following is obvious to you, but ::type is to a metafunction what actual invocation is to a run-time function. So, mpl::end<Constraints> refers to the name of the entity, but does not actually compute its "return value" unless you add the ::type suffix.
I don't think this always holds.
Yeah, it always holds.
For example, I don't need to add the ::type suffix when I make a typedef or when I do an assertion:
typedef mpl::set<A, B> MySet;
There's no metafunction invocation there; you're just stating the name ofa type (mpl::set<A,B>). mpl::set<...> may actually have a ::type member (essentially a typedef for mpl::set<...> itself), but if so it's just there as a convenience for use with constructs like eval_if. mpl::eval_if< predicate , some_metafunction_to_be_evaluated_if_pred_true<X> , mpl::set< ... > > it prevents you from having to write mpl::eval_if< predicate , some_metafunction_to_be_evaluated_if_pred_true<X> , mpl::identity<mpl::set< ... > > ^^^^^^^^^^^^^ >
BOOST_MPL_ASSERT(( mpl::equal<mpl::set<A,B>, mpl::set<A,B> > ));
No explicit metafunction invocation there either... at least, not by you. Again, you're just stating the name of a type: mpl::equal<mpl::set<A,B>, mpl::set<A,B> > That type happens to be a nullary metafunction (any N-ary metafunction, where N>0, with all its arguments filled in, is a nullary metafunction: a class with a nested ::type). Actually the extra set of parens is used by MPL to form a function type: int (mpl::equal<mpl::set<A,B>, mpl::set<A,B> >) MPL strips the int(...) off to get your nullary metafunction, and then it invokes that. Just as with the 2nd or 3rd argument to mpl::eval_if. There's a class of metafunctions that usually don't need to be explicitly invoked: those whose range of results is limited in certain ways. In practice this turns out to mean metafunctions with numeric/boolean results. For example, I could write a metafunction to square a numeric value like this: template <class N> struct square : mpl::integral_c< N::value_type, N::type::value*N::type::value > {}; In what sense is that a metafunction? Well, all MPL IntegralConstants contain a nested ::type returning an equivalent IntegralConstant. So integral_c looks like: template <class T, T x> struct integral_c { typedef T value_type; static T const value = x; typedef integral_c<T,x> type; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }; So integral_c<U,v> is a self-returning nullary metafunction (reach inside to get its ::type, you get itself back). And square<X> inherits most of those properties... enough to make it a conforming IntegralConstant. As a result, you could write mpl::add< square<mpl::int_<3> >::type, mpl::int_<1> >::type ^^^^^^ or mpl::add< square<mpl::int_<3> >, mpl::int_<1> >::type and you'd get the same result. The same applies to all the Boost/TR1 type traits with their boolean result types. That's why you can write either: mpl::if_< boost::is_pointer<X>::type , T // ^^^^^^ , F >::type or mpl::if_< boost::is_pointer<X> // no ::type , T , F >::type and get the same result. Incidentally, it's possible to construct a C++ template metaprogramming system in which ::type (and typename!) is needed much less frequently (http://aspn.activestate.com/ASPN/Mail/Message/boost/2259201). I think Vesa's approach had a lot of promise and I wish we'd had time to pursue it. -- Dave Abrahams Boost Consulting www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com
David Abrahams wrote:
Scott, I hate to say this, but I really think your first problem demonstrated that mpl::set is too broken to use.
Okay, I was pretty much coming to that conclusion myself. For the time being, I'll use vector and be careful about avoiding duplicate entries (practical in my case, because all I'm trying to do is develop a proof of concept). I really wish I could use what used to be referred to as typedef templates (and which now has a more general name and utility in C++0x, as I recall), but I'll swallow my desire for abstraction. I'll just hardwire vector and move on.
Well, excuse me if the following is obvious to you, but ::type is to a metafunction what actual invocation is to a run-time function. So, mpl::end<Constraints> refers to the name of the entity, but does not actually compute its "return value" unless you add the ::type suffix. I don't think this always holds.
Yeah, it always holds.
Thanks for the detailed elaboration, which I'll study when I have a bit more time. You understand this stuff better than I ever will, but I have to say that as a naive new user, BOOST_MPL_ASSERT(( mpl::equal<mpl::set<A,B>, mpl::set<A,B> > )); sure looks like I'm invoking mpl::equal and asserting that the result is true. I will remark that after reading your book and spending probably between 1 and 2 dozen hours trying to write a simple MPL-based program, the rules for when to use the ::type suffix are a complete mystery to me. Originally I just omitted it unless my code didn't compile (assuming, of course, I wanted it to compile -- sometimes the goal of TMP is to prevent something from compiling), but now I've advanced to throwing it in if my MPL program doesn't behave the way I want it to. It's still a black box to me, so if I'm mystified (not uncommon), I try my code both with and without the black box. The result is awful, I'm sure, but speaking only for myself, I think this is a necessary stage in my scramble up the learning cliff. Scott
Scott Meyers <usenet <at> aristeia.com> writes:
The result is awful, I'm sure, but speaking only for myself, I think this is a necessary stage in my scramble up the learning cliff.
I disagree in that this is necessary. It would be nice if you write "Effective MPL" after you got through the struggles (Yes, I do not copy, but really buy the books you write). Thank you for asking the questions I hesitate to ask. OTOH I find it interesting that we run into this problem again and again: the most important thing in boost code is the what and why of the design decisions and the possibility to get insight into the new technologies applied. So what we really need together with the tutorials and the documentation is a wiki about the design and evolution of boost. Then Eric Niebler could explain in depth why he needs "static, aggregate initialization" in proto (see <http://thread.gmane.org/gmane.comp.parsers.spirit.devel/2886/focus=2890>) and why mpl does not fit for proto. Picking the pieces from the groups is awkward. I am trying to rewrite Daixtrose based on mpl, now all the cool programmers of boost, even those of boost consulting, leap ahead without looking back at their very own product. It's not good to read about Mr. Meyers having trouble with one of the most promising C++ libraries of the decade. Markus, wide eyes mesmerized by all those trouble reports about mpl.
on Thu Mar 29 2007, Markus Werle <numerical.simulation-AT-web.de> wrote:
Then Eric Niebler could explain in depth why he needs "static, aggregate initialization" in proto (see <http://thread.gmane.org/gmane.comp.parsers.spirit.devel/2886/focus=2890>) and why mpl does not fit for proto.
MPL doesn't fit for proto? I don't think Eric said that.
I am trying to rewrite Daixtrose based on mpl, now all the cool programmers of boost, even those of boost consulting, leap ahead without looking back at their very own product.
What do you mean, please?
It's not good to read about Mr. Meyers having trouble with one of the most promising C++ libraries of the decade.
Au contraire; it's a good thing. It means he's actually learning something which he used to tell me "makes my head explode." :) -- Dave Abrahams Boost Consulting www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com
Markus Werle wrote:
So what we really need together with the tutorials and the documentation is a wiki about the design and evolution of boost.
Design rationales are good. A wiki might work, but a better place might be in a "Rationale" section in each Boost library's documentation.
Then Eric Niebler could explain in depth why he needs "static, aggregate initialization" in proto (see <http://thread.gmane.org/gmane.comp.parsers.spirit.devel/2886/focus=2890>) and why mpl does not fit for proto.
Static initialization in proto is important so that when DSEL authors declare their global primitives like _1, there are no global initialization order problems. The only problem I had using MPL with proto was compile-time performance problems when using MPL lambdas. In order to keep compile times down, I've replaced as much template meta-programming in proto as possible with preprocessor meta-programming. And proto is a bit of a special case since TMP-heavy libraries are built on top of proto, so the TMP overhead of proto itself should be as small as possible. MPL is a very nice abstraction, but it's not free. Ditto for Fusion. And yes, I measure compile time performance and don't optimize prematurely. :-) -- Eric Niebler Boost Consulting www.boost-consulting.com
[ Sorry, gmane was down on Friday and I could not get access to another nntp access point during weekend ] Eric Niebler <eric <at> boost-consulting.com> writes:
Markus Werle wrote:
So what we really need together with the tutorials and the documentation is a wiki about the design and evolution of boost.
Design rationales are good. A wiki might work, but a better place might be in a "Rationale" section in each Boost library's documentation.
The advantage of a wiki (or any other public read/write access documentation) is that not only the designer of some boost part is in the role of an author for his design rationales but others can easily contribute. More important than the design rationale for boost::XXX is the design rationale for some of the building blocks of it, and since these are used across several distinct library parts, we really need design rationales for _techniques_ used in boost, a meta-boost design rationale. (Common tricks like deep_copy in all its flavours etc.) I sometimes come across a discusion which I feel to be important enough to be cut'n'pasted to a CMS, let it be wiki or joomla or whatever you like ...
Then Eric Niebler could explain in depth why he needs "static, aggregate initialization" in proto (see <http://thread.gmane.org/gmane.comp.parsers.spirit.devel/2886/focus=2890 ) and why mpl does not fit for proto.
Static initialization in proto is important so that when DSEL authors declare their global primitives like _1, there are no global initialization order problems.
The only problem I had using MPL with proto was compile-time
My skills are not such that this piece of information suffices as explanation about what was suboptimal and what was fixed. Going back to your code again ... maybe I catch the idea sometimes. Never saw that trick before. What is better in static type call(Expr const &expr) { type that = {proto::arg(expr)}; return that; } than static type call(Expr const &expr) { type that = proto::arg(expr); return that; } ? performance
problems when using MPL lambdas. In order to keep compile times down, I've replaced as much template meta-programming in proto as possible with preprocessor meta-programming.
Could you publish an article about that? Your article "Conditional Love: Foreach Redux" (http://www.artima.com/cppsource/foreach.html) is a good example about cool things that are hidden in some innocent looking piece of code. Reading the header file never would have exposed the things that matter (at least to me).
And proto is a bit of a special case since TMP-heavy libraries are built on top of proto, so the TMP overhead of proto itself should be as small as possible. MPL is a very nice abstraction, but it's not free. Ditto for Fusion.
This raises 2 questions for me: 1. Could these issues be fixed for mpl and fusion or are you really forced to create your own enhanced versions? 2. If you build your own high performance versions of typelist etc.: isn't fusion and/or mpl a good place to add them instead to "hide" them in proto - below xpressive? <cite url="http://thread.gmane.org/ gmane.comp.parsers.spirit.devel/2886/focus=2890"> > [...] proto::basic_expr<> is the central data container of proto. > It's functionality very much resembles a fusion sequence </cite>
And yes, I measure compile time performance and don't optimize prematurely.
I do not question what you are doing. I am simply looking forward to profit from the fact that you've been already there in compiler hell and are willing to give us some good advice how to survive when we go there, too. I am probably asking too much here. It's only that I am not able to extract this knowledge from source code ... Markus P.S.: Apropos proto: are you planning to introduce a glommable disambiguation mechanism [Geoffrey Furnish]?
on Mon Apr 02 2007, Markus Werle <numerical.simulation-AT-web.de> wrote:
[ Sorry, gmane was down on Friday and I could not get access to another nntp access point during weekend ]
Eric Niebler <eric <at> boost-consulting.com> writes:
Markus Werle wrote:
So what we really need together with the tutorials and the documentation is a wiki about the design and evolution of boost.
Design rationales are good. A wiki might work, but a better place might be in a "Rationale" section in each Boost library's documentation.
The advantage of a wiki (or any other public read/write access documentation) is that not only the designer of some boost part is in the role of an author for his design rationales but others can easily contribute.
I think the next version of the Boost website is slated to have a feature something like the comment system you see here: http://www.djangobook.com/en/beta/chapter05/ -- Dave Abrahams Boost Consulting www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com
David Abrahams wrote:
I think the next version of the Boost website is slated to have a feature something like the comment system you see here:
That's cool. I wonder how that would integrate with the generated docs. Would we need BoostBook support? And are comments wiped with each Boost release? -- Eric Niebler Boost Consulting www.boost-consulting.com
Eric Niebler wrote:
David Abrahams wrote:
I think the next version of the Boost website is slated to have a feature something like the comment system you see here:
Nice, hadn't seen that before :-)
That's cool. I wonder how that would integrate with the generated docs. Would we need BoostBook support?
Maybe. It probably depends on how stable we want to make the comments.
And are comments wiped with each Boost release?
No. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim - grafikrobot/yahoo
Markus Werle wrote:
[ Sorry, gmane was down on Friday and I could not get access to another nntp access point during weekend ]
Eric Niebler <eric <at> boost-consulting.com> writes:
Markus Werle wrote:
So what we really need together with the tutorials and the documentation is a wiki about the design and evolution of boost. Design rationales are good. A wiki might work, but a better place might be in a "Rationale" section in each Boost library's documentation.
The advantage of a wiki (or any other public read/write access documentation) is that not only the designer of some boost part is in the role of an author for his design rationales but others can easily contribute.
True dat.
More important than the design rationale for boost::XXX is the design rationale for some of the building blocks of it, and since these are used across several distinct library parts, we really need design rationales for _techniques_ used in boost, a meta-boost design rationale. (Common tricks like deep_copy in all its flavours etc.)
I sometimes come across a discusion which I feel to be important enough to be cut'n'pasted to a CMS, let it be wiki or joomla or whatever you like ...
Well, Boost has as wiki, so what's stopping you? :-)
Then Eric Niebler could explain in depth why he needs "static, aggregate initialization" in proto (see <http://thread.gmane.org/gmane.comp.parsers.spirit.devel/2886/focus=2890 ) and why mpl does not fit for proto. Static initialization in proto is important so that when DSEL authors declare their global primitives like _1, there are no global initialization order problems.
My skills are not such that this piece of information suffices as explanation about what was suboptimal and what was fixed. Going back to your code again ... maybe I catch the idea sometimes. Never saw that trick before.
What is better in
static type call(Expr const &expr) { type that = {proto::arg(expr)}; return that; }
than
static type call(Expr const &expr) { type that = proto::arg(expr); return that; }
?
Oh, nothing at all. The benefit comes when declaring global primitives. Consider this at namespace scope: namespace my { struct placeholder {}; proto::terminal<placeholder>::type const _ = {{}}; } If terminal<>::type were a type with a constructor, that constructor would have to run *sometime* and the placeholder wouldn't be usable until after that time. Trouble is, C++ makes very few promises about when that *sometime* is. In contrast, the code above requires no runtime initialization. The placeholder just *is*, and there is no time during your program's execution when it is invalid to use it.
The only problem I had using MPL with proto was compile-time performance problems when using MPL lambdas. In order to keep compile times down, I've replaced as much template meta-programming in proto as possible with preprocessor meta-programming.
Could you publish an article about that? Your article "Conditional Love: Foreach Redux" (http://www.artima.com/cppsource/foreach.html) is a good example about cool things that are hidden in some innocent looking piece of code. Reading the header file never would have exposed the things that matter (at least to me).
I don't think that would make for an interesting article. See below.
And proto is a bit of a special case since TMP-heavy libraries are built on top of proto, so the TMP overhead of proto itself should be as small as possible. MPL is a very nice abstraction, but it's not free. Ditto for Fusion.
This raises 2 questions for me:
1. Could these issues be fixed for mpl and fusion or are you really forced to create your own enhanced versions?
I'm not creating enhanced versions of mpl or fusion. I'm using the preprocessor to generate code that would do the same thing as an mpl or fusion algorithm invocation. More below ...
2. If you build your own high performance versions of typelist etc.: isn't fusion and/or mpl a good place to add them instead to "hide" them in proto - below xpressive?
<cite url="http://thread.gmane.org/ gmane.comp.parsers.spirit.devel/2886/focus=2890">
[...] proto::basic_expr<> is the central data container of proto. It's functionality very much resembles a fusion sequence </cite>
I think I've been unclear. In proto, I try to keep template instantiations down. One way I do that is by generating specialization with Boost.PP instead of calling mpl/fusion algorithms. Consider deep_copy(), which essentially applies a transform to each child of a node. There are at least two ways to do this: 1) The Easy Way: Use fusion::transform(). Done! 2) The Hard Way: write N specializations of deep_copy<Node> (where N is the maximum number of children a node can have), and do the transformation on each child directly. The only thing (2) has going for it is that it incurs fewer template instantiations. No fusion iterators, or transform_views, or miscellaneous traits like begin<>, end<>, equal_to<>, or anything else. But would I endorse (2)? No. Just like I wouldn't recommend programming in assembly, unless you really need that extra 5% speed.
And yes, I measure compile time performance and don't optimize prematurely.
I do not question what you are doing. I am simply looking forward to profit from the fact that you've been already there in compiler hell and are willing to give us some good advice how to survive when we go there, too.
I am probably asking too much here. It's only that I am not able to extract this knowledge from source code ...
I'm sorry to disappoint you, but there is probably no way you can profit from proto's innards.
P.S.: Apropos proto: are you planning to introduce a glommable disambiguation mechanism [Geoffrey Furnish]?
I Googled this: http://www.adtmag.com/joop/carticle.aspx?ID=627. IIUC, this is a way to control which sub-expressions can combine with which others, and with which operators. Proto can do this, and its mechanism is much more powerful and elegant, IMNSHO. You define the meta-grammar of your DSEL, and then you tell Proto that only expression types that conform to that meta-grammar are allowed. Operators that produce invalid expressions are not even considered. First read this ("Patterns and Meta-Grammars"): http://tinyurl.com/3akbtb Then this ("Extending Proto"): http://tinyurl.com/2xs222 HTH, -- Eric Niebler Boost Consulting www.boost-consulting.com
Eric Niebler wrote:
Well, Boost has as wiki, so what's stopping you? :-)
U R RIGTH
[...] Oh, nothing at all. The benefit comes when declaring global primitives. Consider this at namespace scope:
namespace my { struct placeholder {}; proto::terminal<placeholder>::type const _ = {{}}; }
If terminal<>::type were a type with a constructor, that constructor would have to run *sometime* and the placeholder wouldn't be usable until after that time. Trouble is, C++ makes very few promises about when that *sometime* is. In contrast, the code above requires no runtime initialization. The placeholder just *is*, and there is no time during your program's execution when it is invalid to use it.
I got it. Thanx. Where in the wiki do we put that piece of information?
The only problem I had using MPL with proto was compile-time performance problems when using MPL lambdas. In order to keep compile times down, I've replaced as much template meta-programming in proto as possible with preprocessor meta-programming.
Could you publish an article about that? Your article "Conditional Love: Foreach Redux" (http://www.artima.com/cppsource/foreach.html) is a good example about cool things that are hidden in some innocent looking piece of code. Reading the header file never would have exposed the things that matter (at least to me).
I don't think that would make for an interesting article. See below.
Seems like leaping ahead is your daily bread. (a rhyme! I claim copyright on that one!) So for you it's crap and for me it's all new and cool stuff.
I'm sorry to disappoint you, but there is probably no way you can profit from proto's innards.
I disagree. If I understand what's inside I probably could make it do what Daixtrose did for me the last years: compile-time differentitaion of arbitray expressions with regard to every variable contained. If proto fits my needs I can put all my rewrite attempts to the garbage bin and spend time on using proto. So proto is what I want to learn and what I want to understand in-depth.
Apropos proto: are you planning to introduce a glommable disambiguation mechanism [Geoffrey Furnish]?
I Googled this: http://www.adtmag.com/joop/carticle.aspx?ID=627. IIUC, this is a way to control which sub-expressions can combine with which others, and with which operators. Proto can do this, and its mechanism is much more powerful and elegant, IMNSHO. You define the meta-grammar of your DSEL, and then you tell Proto that only expression types that conform to that meta-grammar are allowed. Operators that produce invalid expressions are not even considered.
First read this ("Patterns and Meta-Grammars"): http://tinyurl.com/3akbtb
That's for the usenet archive: <http://boost-sandbox.sourceforge.net/libs/proto/doc/html/boost_proto/user_s_guide/pattern_matching_and_meta_grammars.html>
Then this ("Extending Proto"): http://tinyurl.com/2xs222
[ No critique, I simply love links that give me an idea of what was meant even in 10 years when tinyurl is down or lost. Adding this for future readers] <http://boost-sandbox.sourceforge.net/libs/proto/doc/html/boost_proto/user_s_guide/extending_proto.html> Thanks for the links, I take a closer look at that first. best regards, Markus
on Tue Apr 03 2007, Markus Werle <numerical.simulation-AT-web.de> wrote:
I disagree. If I understand what's inside I probably could make it do what Daixtrose did for me the last years: compile-time differentitaion of arbitray expressions with regard to every variable contained.
You shouldn't need to understand proto's implementation in order to do that. That's the whole point of proto! -- Dave Abrahams Boost Consulting www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com
on Thu Mar 29 2007, Scott Meyers <usenet-AT-aristeia.com> wrote:
David Abrahams wrote:
Scott, I hate to say this, but I really think your first problem demonstrated that mpl::set is too broken to use.
Okay, I was pretty much coming to that conclusion myself. For the time being, I'll use vector and be careful about avoiding duplicate entries (practical in my case, because all I'm trying to do is develop a proof of concept). I really wish I could use what used to be referred to as typedef templates (and which now has a more general name and utility in C++0x, as I recall), but I'll swallow my desire for abstraction. I'll just hardwire vector and move on.
Well, excuse me if the following is obvious to you, but ::type is to a metafunction what actual invocation is to a run-time function. So, mpl::end<Constraints> refers to the name of the entity, but does not actually compute its "return value" unless you add the ::type suffix. I don't think this always holds.
Yeah, it always holds.
Thanks for the detailed elaboration, which I'll study when I have a bit more time. You understand this stuff better than I ever will, but I have to say that as a naive new user,
BOOST_MPL_ASSERT(( mpl::equal<mpl::set<A,B>, mpl::set<A,B> > ));
sure looks like I'm invoking mpl::equal and asserting that the result is true.
You're passing a predicate nullary metafunction to the library and asking MPL to invoke it. It's sort of like, at runtime, int f(int, int); # akin to mpl::equal template <class NullaryFunction> int invoke(NullaryFunction g) { return g(); } int result = invoke( boost::bind(f, 3, 5) ); ^^^^^^^^^^^^^^^^^^^^ the nullary function, akin to mpl::equal<mpl::set<A,B>, mpl::set<A,B> > The nice thing about this nullary metafunction is that its type name, "mpl::equal<mpl::set<A,B>, mpl::set<A,B> >" is descriptive of what you're testing, so BOOST_MPL_ASSERT can arrange to have it show up prominently in the error message if the test fails.
I will remark that after reading your book and spending probably between 1 and 2 dozen hours trying to write a simple MPL-based program, the rules for when to use the ::type suffix are a complete mystery to me. Originally I just omitted it unless my code didn't compile (assuming, of course, I wanted it to compile -- sometimes the goal of TMP is to prevent something from compiling), but now I've advanced to throwing it in if my MPL program doesn't behave the way I want it to.
:) Not much of an advance. Try "studying my detailed elaboration" and let me know if that's still the case. -- Dave Abrahams Boost Consulting www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com
David Abrahams wrote:
You're passing a predicate nullary metafunction to the library and asking MPL to invoke it. It's sort of like, at runtime,
int f(int, int); # akin to mpl::equal
template <class NullaryFunction> int invoke(NullaryFunction g) { return g(); }
int result = invoke( boost::bind(f, 3, 5) ); ^^^^^^^^^^^^^^^^^^^^ the nullary function, akin to mpl::equal<mpl::set<A,B>, mpl::set<A,B> >
I find it a lot easier to understand if I focus on the fact that, unlike runtime asserts, which conceptually take boolean values, BOOST_MPL_ASSERT takes a function, which it itself invokes. This is, to me, a *major* change. It's the difference between bool f(); // some function assert( f() ); // the way asserts work now assert( f ); // the way asserts would work if they worked like MPL asserts I think a lot of the trouble I have is due to a lack of documentation explaining the differences between what I'm used to with runtime C++ and how MPL does things. IME, it's not that the MPL documentation is incorrect, it's just that the reference manual is, well, a reference manual. So I skim over the fact that BOOST_MPL_ASSERT takes a metafunction, because I'm thinking, "I already know how asserts work." But I *don't* know how MPL asserts work, because they use a completely different conceptual model, yet they use the same vocabulary. I'm not blaming the reference manual here. It does what it should do. And I'm not blaming your book. But note in your book how on page 160 you show that BOOST_STATIC_ASSERT takes an *expression* and on page 162 you show how BOOST_MPL_ASSERT takes a *metafunction*, yet the only thing you explicitly call the reader's attention to is the need for double parentheses in the MPL assert. At least in my case, it was really easy to fail to note the shift from an expression (what I was used to) to a function (quite a different thing). Scott
on Thu Mar 29 2007, Scott Meyers <usenet-AT-aristeia.com> wrote:
David Abrahams wrote:
You're passing a predicate nullary metafunction to the library and asking MPL to invoke it. It's sort of like, at runtime,
int f(int, int); # akin to mpl::equal
template <class NullaryFunction> int invoke(NullaryFunction g) { return g(); }
int result = invoke( boost::bind(f, 3, 5) ); ^^^^^^^^^^^^^^^^^^^^ the nullary function, akin to mpl::equal<mpl::set<A,B>, mpl::set<A,B> >
I find it a lot easier to understand if I focus on the fact that, unlike runtime asserts, which conceptually take boolean values, BOOST_MPL_ASSERT takes a function, which it itself invokes. This is, to me, a *major* change. It's the difference between
bool f(); // some function
assert( f() ); // the way asserts work now
assert( f ); // the way asserts would work if they worked like MPL asserts
Right.
I think a lot of the trouble I have is due to a lack of documentation explaining the differences between what I'm used to with runtime C++ and how MPL does things. IME, it's not that the MPL documentation is incorrect, it's just that the reference manual is, well, a reference manual. So I skim over the fact that BOOST_MPL_ASSERT takes a metafunction, because I'm thinking, "I already know how asserts work." But I *don't* know how MPL asserts work, because they use a completely different conceptual model, yet they use the same vocabulary.
I'm not blaming the reference manual here. It does what it should do. And I'm not blaming your book. But note in your book how on page 160 you show that BOOST_STATIC_ASSERT takes an *expression* and on page 162 you show how BOOST_MPL_ASSERT takes a *metafunction*, yet the only thing you explicitly call the reader's attention to is the need for double parentheses in the MPL assert. At least in my case, it was really easy to fail to note the shift from an expression (what I was used to) to a function (quite a different thing).
Well, you have a very good point there. It would have been good to point that out more explicitly. -- Dave Abrahams Boost Consulting www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com
David Abrahams wrote:
:) Not much of an advance. Try "studying my detailed elaboration" and let me know if that's still the case.
Things are better, thanks primarily to Joaquín M López Muñoz's helpful explanation. Part of my confusion, I think, can be traced to the design decision behind this comment of yours:
mpl::set<...> may actually have a ::type member (essentially a typedef for mpl::set<...> itself), but if so it's just there as a convenience for use with constructs like eval_if.
...
There's a class of metafunctions that usually don't need to be explicitly invoked: those whose range of results is limited in certain ways. In practice this turns out to mean metafunctions with numeric/boolean results.
The convenience you write of is real, but it also means that some metafunction invocations need no ::type, but others do. Without understanding the rules in some detail, it's hard to figure out when ::type is required. The MPL design chooses convenience over consistency, and at the beginning of the learning curve, my personal opinion is that consistency is more important than convenience. For experienced users, of course, the balance point may be -- probably is -- at a different location. Scott
on Thu Mar 29 2007, Scott Meyers <usenet-AT-aristeia.com> wrote:
David Abrahams wrote:
:) Not much of an advance. Try "studying my detailed elaboration" and let me know if that's still the case.
Things are better, thanks primarily to Joaquín M López Muñoz's helpful explanation. Part of my confusion, I think, can be traced to the design decision behind this comment of yours:
mpl::set<...> may actually have a ::type member (essentially a typedef for mpl::set<...> itself), but if so it's just there as a convenience for use with constructs like eval_if.
...
There's a class of metafunctions that usually don't need to be explicitly invoked: those whose range of results is limited in certain ways. In practice this turns out to mean metafunctions with numeric/boolean results.
The convenience you write of is real, but it also means that some metafunction invocations need no ::type, but others do. Without understanding the rules in some detail, it's hard to figure out when ::type is required. The MPL design chooses convenience over consistency, and at the beginning of the learning curve, my personal opinion is that consistency is more important than convenience. For experienced users, of course, the balance point may be -- probably is -- at a different location.
You're still free to use all the unnecessary typename ... ::type incantations if you want to be consistent and safe. Vesa Karvonen's "Lazy MPL," which I mentioned in an earlier posting, would probably be much easier overall because it only needs ::type in a very few well-defined places. -- Dave Abrahams Boost Consulting www.boost-consulting.com Don't Miss BoostCon 2007! ==> http://www.boostcon.com
participants (8)
-
"JOAQUIN LOPEZ MU?Z"
-
David Abrahams
-
David Klein
-
Eric Niebler
-
Markus Werle
-
Ovanes Markarian
-
Rene Rivera
-
Scott Meyers