[review][mp11] Formal review of Mp11
The formal review of Peter Dimov's Mp11 library is scheduled for July 15 - July 24, 2017 [1]. Mp11 is a C++11 metaprogramming library for compile-time manipulation of data structures that contain types. It’s based on template aliases and variadic templates and implements the approach outlined in the article "Simple C++ metaprogramming" [2] and its sequel [3]. These articles are useful background information for the review. * Mp11 aims to make simple usage simple, and to support complex usage without complicating the simple use cases. * Mp11 works on any type-list, whether its own type-list mp_list, or standard type-lists such as std::tuple and std::variant, or user-defined type-lists. * Mp11 works with any meta-function, such as C++11 or Boost type-traits, or user-defined type-traits. Mp11 can be found here: * Documentation: https://rawgit.com/pdimov/mp11/master/doc/html/mp11.html * Source code: https://github.com/pdimov/mp11/tree/master Please answer the following questions in your review [4]: 1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity. 2. What is your evaluation of the design? 3. What is your evaluation of the implementation? 4. What is your evaluation of the documentation? 5. What is your evaluation of the potential usefulness of the library? 6. Did you try to use the library? With what compiler? Did you have any problems? 7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? 8. Are you knowledgeable about the problem domain? [1] http://www.boost.org/community/review_schedule.html [2] http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html [3] http://pdimov.com/cpp2/simple_cxx11_metaprogramming_2.html [4] http://www.boost.org/community/reviews.html
On Sat, Jul 15, 2017 at 3:19 AM, Bjorn Reese via Boost <boost@lists.boost.org> wrote:
* Documentation: https://rawgit.com/pdimov/mp11/master/doc/html/mp11.html
Is there a rationale explaining how mp11 differentiates itself from the numerous other metaprogramming libraries, some in Boost? Why does Boost need yet another one of these things?
Vinnie Falco wrote:
* Documentation: https://rawgit.com/pdimov/mp11/master/doc/html/mp11.html
Is there a rationale explaining how mp11 differentiates itself from the numerous other metaprogramming libraries, some in Boost? Why does Boost need yet another one of these things?
Mp11 differentiates itself from Boost.MPL by being based on C++11 constructs such as template aliases and variadic templates. Compared to Boost.Hana, Mp11 offers pure type-based metaprogramming. This more limited scope in practice translates to more compilers supported and faster compile times. If we look outside Boost, compared to Metal, the main difference is that Mp11 is generic with respect to the List and Number concepts; it works with std::tuple, std::pair, std::variant, the lot, and it supports type traits of the is_const variety directly.
On Sat, Jul 15, 2017 at 3:19 AM, Bjorn Reese via Boost <boost@lists.boost.org> wrote:
The formal review of Peter Dimov's Mp11 library is scheduled for July 15 - July 24, 2017 [1].
* The documentation states: "mp_if_c<true, T, E…> is an alias for T. mp_if_c<false, T, E> is an alias for E. Otherwise, the result is a substitution failure." Should that read "mp_if_c<false, T, E...>"? If not, why? And shouldn't the document address it?
Vinnie Falco wrote:
* The documentation states: "mp_if_c<true, T, E…> is an alias for T. mp_if_c<false, T, E> is an alias for E. Otherwise, the result is a substitution failure."
Should that read "mp_if_c<false, T, E...>"? If not, why?
No. The idea here is that mp_if_c can be used in two ways. One is mp_if_c<Cond, T, E>, which gives either T or E depending on Cond. The other is mp_if_c<Cond, T>, which is the same as std::enable_if_t<Cond, T> - gives T when Cond, otherwise a substitution failure. So mp_if_c<true, T> is T mp_if_c<true, T, E> is T mp_if_c<false, T, E> is E Aliases can't be specialized, so it's not possible to implement just the above, which is why it takes a parameter pack for E... and does mp_if_c<true, T, E...> is T mp_if_c<false, T, E> is E which for the three cases we're interested in gives the results we need.
On 07/15/17 15:47, Peter Dimov via Boost wrote:
Vinnie Falco wrote:
* The documentation states: "mp_if_c<true, T, E…> is an alias for T. mp_if_c<false, T, E> is an alias for E. Otherwise, the result is a substitution failure."
Should that read "mp_if_c<false, T, E...>"? If not, why?
No.
The idea here is that mp_if_c can be used in two ways. One is mp_if_c<Cond, T, E>, which gives either T or E depending on Cond. The other is mp_if_c<Cond, T>, which is the same as std::enable_if_t<Cond, T> - gives T when Cond, otherwise a substitution failure.
So
mp_if_c<true, T> is T mp_if_c<true, T, E> is T mp_if_c<false, T, E> is E
Aliases can't be specialized, so it's not possible to implement just the above, which is why it takes a parameter pack for E... and does
mp_if_c<true, T, E...> is T mp_if_c<false, T, E> is E
which for the three cases we're interested in gives the results we need.
Why not separate the two use case into two primitives? We usually want one or the other and different tools would make sense, at least for documenting purpose.
I think for people who don't use packs often the fact that the E... pack is being used essentially to make E optional is a source of confusion. The way it's implemented, you can also pass more than one type in the else to mp_if_c when the condition is true, but not if it's false. Maybe you couuld separate the detail::mp_if_c_impl into 2 specializations for the true case, one with a single else and one with no else type. Then passing multiple else types would always fail substituation whether or not the condition is true or false. Then I'd change the documentation to list out those two separate true cases and make no mention of the E..., or if you want you could say mp_if_c<bool,T,E1,E2,Es...> will always fail substituation explicitly in the documentation (but that again just seems like adding confusion.) - Josh On Sat, 15 Jul 2017, Andrey Semashev via Boost wrote:
On 07/15/17 15:47, Peter Dimov via Boost wrote:
Vinnie Falco wrote:
* The documentation states: "mp_if_c<true, T, E…> is an alias for T. mp_if_c<false, T, E> is an alias for E. Otherwise, the result is a substitution failure."
Should that read "mp_if_c<false, T, E...>"? If not, why?
No.
The idea here is that mp_if_c can be used in two ways. One is mp_if_c<Cond, T, E>, which gives either T or E depending on Cond. The other is mp_if_c<Cond, T>, which is the same as std::enable_if_t<Cond, T> - gives T when Cond, otherwise a substitution failure.
So
mp_if_c<true, T> is T mp_if_c<true, T, E> is T mp_if_c<false, T, E> is E
Aliases can't be specialized, so it's not possible to implement just the above, which is why it takes a parameter pack for E... and does
mp_if_c<true, T, E...> is T mp_if_c<false, T, E> is E
which for the three cases we're interested in gives the results we need.
Why not separate the two use case into two primitives? We usually want one or the other and different tools would make sense, at least for documenting purpose.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Sat, Jul 15, 2017 at 5:57 AM, Andrey Semashev via Boost <boost@lists.boost.org> wrote:
Why not separate the two use case into two primitives? We usually want one or the other and different tools would make sense, at least for documenting purpose.
Now that its been explained, I appreciate that `mp_if_c` has additional functionality. I think its a mistake to split it up when it is so elegantly expressed in its current form. I just wish there was more explanation in the reference for a novice such as myself. Thanks
Andrey Semashev wrote:
The idea here is that mp_if_c can be used in two ways. One is mp_if_c<Cond, T, E>, which gives either T or E depending on Cond. The other is mp_if_c<Cond, T>, which is the same as std::enable_if_t<Cond, T> - gives T when Cond, otherwise a substitution failure. ...
Why not separate the two use case into two primitives? We usually want one or the other and different tools would make sense, at least for documenting purpose.
One could go either way on that, I suppose. It makes sense because mp_if<C, T, E> gives you T when C, otherwise E, and mp_if<C, T> gives you T when C, otherwise <nothing>. "If C, T, otherwise, ..."
This is my review of the mp11 library. I. DESIGN --------- The design of the library is well organized and well thought out. The rationale of prepending `mp_` to all identifiers makes sense and results in less confusion. It lets lets you write using namespace boost::mp11; with little chance of stepping on anything in the enclosing namespace. Function names are consistent with their run-time equivalents. The decision to recognize almost any class template as a list is brilliant and speaks to the power of the library to express ideas in flexible and concise ways. II. IMPLEMENTATION ------------------ I looked at each public header file. Its really amazing how C++11 allows the compact expression of metafunctions using parameter packs; this is reflected in mp11's implementation. Many of the functions are simple, intuitive one-liners. There are helpful comments such as this, to aid users who get compilation errors: <https://github.com/pdimov/mp11/blob/8e1d23b11f25c3864593b6fc5a8449306e17ea23/include/boost/mp11/list.hpp#L28> Everything is `constexpr` where possible, and the author has implemented straightforward optimizations to reduce compilation times for the heavier functions, such as these: <https://github.com/pdimov/mp11/blob/8e1d23b11f25c3864593b6fc5a8449306e17ea23/include/boost/mp11/detail/mp_count.hpp#L46> <https://github.com/pdimov/mp11/blob/8e1d23b11f25c3864593b6fc5a8449306e17ea23/include/boost/mp11/detail/mp_append.hpp#L81> A casual inspection of the source files should be enough to give any potential user confidence in the quality of the library (assuming they are somewhat familiar with the domain). The implementation is in sharp contrast to the "template soup" I usually encounter when looking at Boost source code. For example, here's Hana: <https://github.com/boostorg/hana/blob/e507494b5f7f51026e8afa317b3ca2c058a4d290/include/boost/hana/any_of.hpp#L58> and here's mp11 <https://github.com/pdimov/mp11/blob/8e1d23b11f25c3864593b6fc5a8449306e17ea23/include/boost/mp11/algorithm.hpp#L861> III. DOCUMENTATION ------------------ The documentation is spartan and not for a beginner like me. There is little explanation on many of the functions (most are single sentences), and a dearth of examples. Most of the reference is a laundry-list of valid expressions with minimal exposition. I found this in a surprising contrast to the linked articles, which were brilliantly written, engaging, informative, and exciting. The author explained that more examples are coming for the reference items. Here's an example of the disparity. The documentation for mp_all has a lot of exposition relative to other reference items, and it includes a good chunk of example code: mp_all<T…> mp_all<T…> is mp_true if mp_to_bool<U> is mp_true for all types U in T…, mp_false otherwise. Same as mp_and, but does not perform short-circuit evaluation. mp_and<mp_false, void> is mp_false, but mp_all<mp_false, void> is an error because void does not have a nested value. The upside is that mp_all is potentially faster and does not mask substitution failures as mp_and does. Code Example 62. mp_all behavior <https://github.com/pdimov/mp11/blob/8e1d23b11f25c3864593b6fc5a8449306e17ea23/doc/mp11/function.adoc#mp_allt> Now compare that with the documentation for mp_replace_at_c: mp_replace_at_c<L, I, W> Replaces the element of L at zero-based index I with W and returns the result. <https://github.com/pdimov/mp11/blob/8e1d23b11f25c3864593b6fc5a8449306e17ea23/doc/mp11/algorithm.adoc#mp_replace_at_cl-i-w> I realize that seasoned metaprogrammers will likely have no trouble at all understanding these terse descriptions and lack of examples but they aren't doing beginners any favors. There are things missing from the documentation that I would expect or hope to find, as some of them may be important to potential users and others are just good marketing which could help to revive the dying Boost: * Comparison to other libraries - How is this different from Hana? MPL? * Limits - Which functions work with almost limitless type lists? - Which functions might require care in the size of type lists? * Speed - Benchmarks. The doc could link to http://metaben.ch/ - Which functions might become slow? The type list limits are compiler-dependent so exact numbers aren't expected, but at least guidance for where someone might encounter problems could be provided to aid in the inevitable troubleshooting. In my opinion, the documentation under-sells the library, I think more could be done to increase the conversion ratio (the fraction of users who decide to use the library after reading the documentation). I can see that metaprogramming gurus who read the Overview will immediately see the advantage of allowing L to be any class template, but I rather doubt that beginners will see that - this should be explained in a bit more direct terms. Here's a rough draft with some sizzle: "mp11 is easier to use than other metaprogramming libraries because it leverages C++11 and allows the use of any class template to express a type list. Programs that use mp11 over other libraries will be easier to read, compile faster, and require fewer lines of code." IV. PRACTICAL USE ----------------- The library appears useful for its intended purpose. I looked through my own code to find metafunctions that could be expressed using mp11 instead. The result of this work can be found here: <https://github.com/vinniefalco/Beast/tree/mp11> <https://github.com/vinniefalco/Beast/commit/d0385e70fa45c675c617dcd192426e059bc7941d> I stumbled a bit in the documentation trying to figure out how to form my expressions but once I did the library performed wonderfully. I liked how `mp_repeat_c` was a more general replacement of my `repeat_tuple`. I spent about two hours with it. Its a shame that so few of Beast's metafunctions fell into the domain of problems that mp11 solves but that's an issue with my library and not mp11. I am somewhat familiar with the problem domain, having done some light manipulation of type lists. V. VERDICT ---------- I have seen various metaprogramming libraries over the years, such as MPL and Hana. Neither of them jumped out at me as something easy to use or immediately helpful. Despite all of my aforementioned complaints regarding the documentation, mp11 remains the ONLY metaprogramming library I have ever had success at in applying to my own programs, and the only metaprogramming library I would consider taking as a dependency. For this reason, mp11 should be ACCEPTED. Thanks
A casual inspection of the source files should be enough to give any potential user confidence in the quality of the library (assuming they are somewhat familiar with the domain). The implementation is in sharp contrast to the "template soup" I usually encounter when looking at Boost source code. For example, here's Hana:
and here's mp11
That's because they do significantly different things and pose significantly different constraints on input the types. Hana's any_of is short-circuiting, works on compile-time infinite sequences, and works on runtime values as well as compile-time values. mp11 has none of that (because that's out of scope for the library, except for short-circuiting which I was surprised it did not provide). Louis -- View this message in context: http://boost.2283326.n4.nabble.com/review-mp11-Formal-review-of-Mp11-tp46968... Sent from the Boost - Dev mailing list archive at Nabble.com.
This is my review of Mp11. As I see it, an important use niche for this library is to allow for phasing out of Boost.MPL in C++11 compilers, so my review is heavily biased by this perspective. To get acquainted with the lib, I made the little experiment of porting Boost.MultiIndex from Boost.MPL to Mp11 , results can be seen at: https://github.com/joaquintides/multi_index_mp11 https://github.com/joaquintides/multi_index_mp11/commit/91f2b32cf5a27b3aeaaf... The process was relatively straightforward, 9x% of it was boilerplate substitution of the form s/[typename] mpl::xx<...>::type/mp11::mp_xx<...>. As for the rest, no serious problem was found. Compile times dropped slightly, maybe around 5-10%, but I did't measure rigurously. 1 DESIGN Mp11 makes the very sensible choice of coding metafunctions/type lists as (variadic) class/alias templates. From this principle everything else follows quite naturally. I also commend dispensing with Boost.MPL iterators and using indexes instead. 1.1 I really don't like the mp_ prefix. I understand this is meant to minimize clashes when using namespace::mp11, but the same purpose can be served by simply using namespace mp=boost::mp11. Having to add this pesky mp_ thing always got in the way of seamless porting, for no real benefit. To add more confusion, some functions (those in integer_sequence.hpp and tuple.hpp) don't have the prefix. 1.2 Why are quoted metafunctions codified with a fn member rather than Boost.MPL-honored apply? Why call it quoted metafunctions rather than metafunction classes, as Boost.MPL does? 1.3 I find that these metafunctions are missing/misplaced: pop_back at/at_c (should be in list.hpp) insert/insert_c/erase/erase_c (should be in list.hpp) replace_at/replace_at_c clear (should be in list.hpp) 1.4 Tuple operations are named differently from their C++14/17 counterparts to "avoid ambiguities when both are visible or in unqualified calls". Yet, this policy is not followed in integer_sequence.hpp. I'd rather go the latter way. 1.5 Treatment of sets and maps is very dissimilar. Why mp_map_find but no mp_set_find? Why mp_set_push_(back|front) but no mp_map_push_(back|front)? Why mp_map_erase but no mp_set_erase? I think both interfaces should have the same functions, except possibly mp_map_(replace|update). 1.5.1 I think that, for consistency, mp_map_find should return an index (like mp_find) rather than the element or void. 1.6 Boost.MPL sequences simulate variadic behavior by having boost::mpl::na-defaulted type parameters. As a consequence, the following is somehow unexpected: std::cout<<mp_size<boost::mpl::vector<int,char>>::value<<"\n"; //prints 20 Porting from / coexisting with Boost.MPL would be greatly aided by some utility function to convert Boost.MPL sequences to Mp11 lists: template<typename MplSequence> struct mp_mpl_sequence_impl; template<template<typename...> class M,typename... T> struct mp_mpl_sequence_impl<M<T...>> { using type=mp_remove<mp_list<T...>,boost::mpl::na>; }; template<typename MplSequence> using mp_mpl_sequence=typename mp_mpl_sequence_impl<MplSequence>::type; ... std::cout<<mp_size< mp_mpl_sequence<boost::mpl::vector<int,char>>>::value<<"\n"; // prints 2 1.7 mp_eval[_xx] functions should accept metafunctions in both their true and false branches. As it stands now, we'd have to write stuff like using l1=mp_if_c< b, mp_list<mp_quote<F>,type1,type2>, mp_list<mp_quote<G>,type3,type4>>; using l2=mp_apply_q<mp_first<l1>,mp_rest<l1>>; to emulate (the as of yet non-existent) using l2=mp_eval_if_c<b,F,type1,type2,G,type3,type4>; The current behavior would be served by the new interface with little overhead: (current) mp_eval_if_c<b,T,F,U...> (new) mp_eval_if_c<b,mp_identity_t,T,F,U...> I have to say I don't know how to apply this new behavior to mp_eval_if_q; thinking out loud, we'd need something like mp_eval_if_q<B,Q1,U1...,mp_else,Q2,U2...> which doesn't look too pretty. A more exotic alternative could be mp_eval_if_q<B,Q1(U1...),Q2(U2...)> 1.7.1 Why there's no eval_if_q_c (or eval_if_c_q?) 2 IMPLEMENTATION I had just a cursory look at the code, but everything seems clean and reasonably straightforward. I like the fact that C++14/17 features are taken advantage of when available (e.g. variadic fold expressions). There are many lines wider than 80 chars, but I think this is OK with current Boost guidelines. Testing seems pretty exhaustive. I find it surprising that there's so much boilerplate code repetition in the testing code --I'd have expected Mp11 itself to be used to generate extensive test cases automatically. 3 DOCUMENTATION This is IMHO the weakest part of this submission. I have concerns with both the tutorial and the reference. 3.1 TUTORIAL With template metaprogramming, one has to ask whether a tutorial for a metaprogamming lib should be oriented towards explaining *metaprogramming* or rather the particular design of the library. Mp11 documentation seems to try both ways, and does not excel in either of them. 3.1.1 Examples are heavy handed and do not focus on why Mp11 is useful --too much discussion goes into the particular difficulties of the use cases studied. I consider myself a reasonably advanced metaprogrammer and found it difficult (and, ultimately, useless) to follow the exposition. For a newbie, the examples are just impenetrable; for a seasoned metaprogrammer wanting to learn Mp11, she's better off reading the definitions section only and then jumping into the reference. I suggest taking a look at Brigand tutorials, which are excellent at explaining the lib to entry-level metaprogrammers in a step-by-step fashion. I think the key is Brigand tutorials don't try to tackle industry-grade metaprogramming problems: they just accompany the reader along toy use cases, and that's really enough. 3.1.2 The definitions section, by contrast, is too terse and at points vague to the verge of incorrection: - It is not "template class" but "class template". - It is not made clear whether a list is a class/alias template (say, mp_list) or rather an instantiation of this template (mp_list<int,char>). Same goes for metafunctions. The confusion extends to the reference, where class templates and their instantiations are named the same, for instance: template<class L> using mp_reverse = /*...*/; mp_reverse<L<T1, T2, …, Tn>> is L<Tn, …, T2, T1>. - The above is not a legal subtlety: as it stands, the definition for set doesn't make sense, as it implies that a set is a *class template* that somehow enforces unicity of template type parameters. Same goes for maps. - All in all, seems like the author's intention is to define lists (and sets and maps) as *template class instantiations*: if this is the case then it has to be explained how two different lists (say, the result of pushing back a type to a list) are connected through their common class template. - By contrast, seems like the intention is to define a metafunction as a class/alias template rather than its instantiations (e.g. mp_size is a metafunction but mp_size<my_list> is not). If this is the case, then the assertion that "Another distinguishing feature of this approach is that lists (L<T…>) have the same form as metafunctions (F<T…>) and can therefore be used as such." is false. - There's a latent concept that goes undocumented, namely lists resulting from the instantiation of a non-variadic class/alias template (e.g. std::pair). Some Mp11 metafunctions are not applicable to them (e.g. mp_push_back), but this is not clearly documented. To be clear, I'm not advocating mathematical rigor, but the current fuzziness is confusing at best and can impede understanding at worst. 3.2 REFERENCE In general, the reference is too terse and overlooks important details. 3.2.1 A "Requires" clause should be added to many entries. For example, it is not clear what happens with mp_at_c<L, I> when I is out of bounds (it does not compile). Another example: mp_pop_front<std::pair<int,bool>> does not compile because std::pair<int,bool> is not a "variadic sequence" (missing concept here). 3.2.2 A complexity section should be added, where complexity is number of of template instantiations. For instance, I don't know how costly set operations are. 3.3 PERFORMANCE As already suggested in another review, links to metaben.ch could be added. 4 WHY ANOTHER METAPROGRAMMING LIBRARY? The landscape of C++ metaprogramming libs is quite populated. These libs can be classified into (to follow metaben.ch's terminology) heterogeneous (Boost.Fusion, Boost.Hana) and pure-type (Boost.MPL, Mp11, Brigand, Metal, Kvasir). To be fair, we're dealing here with acceptance of a library into Boost, so we should only be really concerned about intra-Boost clashes, which leaves us with Boost.Fusion, Boost.Hana, Boost.MPL and Mp11. In my opinion, heterogeneous and pure-type libs have different scopes and entry barriers: for the kind of intra-lib scaffolding I find myself doing, I'd rather go with a pure-type metaprogramming lib before buying the complexities of Boost.Hana. In this respect, a replacement for old and clunky Boost.MPL is most welcome, and this is the place that Mp11 can occupy. This said, we can also look out to the wider world and recognize that Mp11 and Brigand have the very same design philosophy and come up with solutions that are exactly interchangeable. It would be great if we could find a way that efforts behind Mp11 and Brigand could be coordinated to come up with something bigger that could serve as Boost's pure-type metaprogramming library as well as a non-Boost lib. To be honest, I don't have a clue how this could be done, or whether there's willingness to collaborate among Mp11's and Brigand's authors. 5 ANSWERS TO REVIEW QUESTIONS * Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity. My vote is for CONDITIONAL ACCEPTANCE dependent upon proper addressing of, at least, mp_list<_1_1, _1_2, _1_4, _1_5, _1_7, _3_1_2, _3_2_1> Of course I'd like all points of my review to be discussed, but I feel only those listed above are important enough to hold acceptance. * What is your evaluation of the design? * What is your evaluation of the implementation? * What is your evaluation of the documentation? * What is your evaluation of the potential usefulness of the library? All addressed above, hopefully. * Did you try to use the library? With what compiler? Did you have any problems? Yes. VS 2015. No problems. * How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? 5-6 hours for the porting exercise, 4-5 hours for the review. * Are you knowledgeable about the problem domain? I've been metaprogramming for 14 years. Thanks to Peter Dimov for his submission of Mp11 and for being such an active contributor to Boost during so many years. Joaquín M López Muñoz
Joaquin M López Muñoz wrote:
1.1 I really don't like the mp_ prefix. I understand this is meant to minimize clashes when using namespace::mp11, but the same purpose can be served by simply using namespace mp=boost::mp11.
Well... it's also meant to make it possible to move mp11 into std without changes. :-)
1.3 I find that these metafunctions are missing/misplaced: pop_back
pop_back is not provided for the same reason std::vector doesn't provide pop_front - it's much less efficient than its siblings, and providing it could mislead people into thinking that it's equally efficient.
at/at_c (should be in list.hpp) insert/insert_c/erase/erase_c (should be in list.hpp) replace_at/replace_at_c
The partitioning is roughly based on whether the operation can be trivially expressed (f.ex. mp_second<L<T1, T2, T...>> -> T2), or requires a more elaborate implementation (as all primitives taking an arbitrary index require.)
clear (should be in list.hpp)
mp_clear could be in list, yes.
1.5 Treatment of sets and maps is very dissimilar. Why mp_map_find but no mp_set_find? Why mp_set_push_(back|front) but no mp_map_push_(back|front)? Why mp_map_erase but no mp_set_erase? I think both interfaces should have the same functions, except possibly mp_map_(replace|update).
Set operations are only provided for the cases where the generic list-based algorithm would be unsuitable or less efficient.
Porting from / coexisting with Boost.MPL would be greatly aided by some utility function to convert Boost.MPL sequences to Mp11 lists:
... This unfortunately doesn't work in general - as Bruno Dutra explained to me - because MPL operations on, f.ex. mpl::vector, do not necessarily return an mpl::vector.
1.7.1 Why there's no eval_if_q_c (or eval_if_c_q?)
The purpose of the _q forms is twofold; one, to enable passing a quoted metafunction, and two, to be metafunctions themselves (that is, they only take type parameters.) _c_q would meet the first goal but not the second. This by itself does not preclude its inclusion, but, while the ability to omit ::value is convenient, it's not indispensable.
- It is not "template class" but "class template".
Yes, it's actually "template class", meaning a class template instantiation. A template class is a class; a class template is a template. Too subtle, perhaps, but this is correct terminology.
3.2.1 A "Requires" clause should be added to many entries.
That's correct. I'll be nailing the requirements down one by one. The choice in each case is between a hard error and a substitution failure, and it is not a trivial choice. (Although Bruno would disagree. :-) ) Thank you for the review!
El 17/07/2017 a las 1:06, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
1.1 I really don't like the mp_ prefix. I understand this is meant to minimize clashes when using namespace::mp11, but the same purpose can be served by simply using namespace mp=boost::mp11.
Well... it's also meant to make it possible to move mp11 into std without changes. :-)
We already have subnamespaces in std (e.g. std::chrono), so your standardization plan could be realized as s/boost/std.
1.3 I find that these metafunctions are missing/misplaced:
pop_back
pop_back is not provided for the same reason std::vector doesn't provide pop_front - it's much less efficient than its siblings, and providing it could mislead people into thinking that it's equally efficient.
Understood.
at/at_c (should be in list.hpp) insert/insert_c/erase/erase_c (should be in list.hpp) replace_at/replace_at_c
The partitioning is roughly based on whether the operation can be trivially expressed (f.ex. mp_second<L<T1, T2, T...>> -> T2), or requires a more elaborate implementation (as all primitives taking an arbitrary index require.)
I see. My assumption was that list.hpp would contain mp equivalents for std container member functions, while algorithm.hpp was home to mp translations of <algorithm> functions. Maybe an intro for each header in the reference can help orient readers.
[...]
1.5 Treatment of sets and maps is very dissimilar. Why mp_map_find but no mp_set_find? Why mp_set_push_(back|front) but no mp_map_push_(back|front)? Why mp_map_erase but no mp_set_erase? I think both interfaces should have the same functions, except possibly mp_map_(replace|update).
Set operations are only provided for the cases where the generic list-based algorithm would be unsuitable or less efficient.
I'm not sure this observation entirely addresses my question. * mp_set_find is not provided because mp_find does the job --OK, but consider my point 1.5.1 then. * If mp_set_push_(back|front) is provided (no suitable list-based algorithm), why not mp_map_push_(back|front)?
Porting from / coexisting with Boost.MPL would be greatly aided by some utility function to convert Boost.MPL sequences to Mp11 lists:
...
This unfortunately doesn't work in general - as Bruno Dutra explained to me - because MPL operations on, f.ex. mpl::vector, do not necessarily return an mpl::vector.
Something more generic can be provided struct mp_mpl_sequence_folder { template<typename T,typename L> struct apply{using type=mp_push_front<T,L>;}; }; template<typename MplSequence> struct mp_mpl_sequence_impl:boost::mpl::reverse_fold< MplSequence, mp_list<>, mp_mpl_sequence_folder
{};
template<typename MplSequence> using mp_mpl_sequence=typename mp_mpl_sequence_impl<MplSequence>::type; with faster specializations for boost::mpl::vector and other MPL sequences.
[...]
- It is not "template class" but "class template".
Yes, it's actually "template class", meaning a class template instantiation. A template class is a class; a class template is a template. Too subtle, perhaps, but this is correct terminology.
I fail to see "template class" defined in N4296 (one ocurrence only in [sequences.general], and seems an editorial typo as it obviously refers to class templates, and a handful of ocurrences of "non-template class"). Additionally: * "A metafunction is a class template or a template alias [...]": s/"template alias"/"alias template"? * "mp_unique<L> returns a list of the same type as L [...]": being "the same type" needs definition. This is found in other places throughout the documentation. Joaquín M López Muñoz
Joaquin M López Muñoz wrote:
El 17/07/2017 a las 1:06, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
1.1 I really don't like the mp_ prefix. I understand this is meant to minimize clashes when using namespace::mp11, but the same purpose can be served by simply using namespace mp=boost::mp11.
Well... it's also meant to make it possible to move mp11 into std without changes. :-)
We already have subnamespaces in std (e.g. std::chrono), so your standardization plan could be realized as s/boost/std.
It could if I wanted to propose std::mp::if_, but I don't. I want to propose std::mp_if. I know that this makes the library more difficult to use in other Boost libraries where there's no convenient place to put the using directive. :-/
Set operations are only provided for the cases where the generic list-based algorithm would be unsuitable or less efficient.
I'm not sure this observation entirely addresses my question.
* mp_set_find is not provided because mp_find does the job --OK, but consider my point 1.5.1 then. * If mp_set_push_(back|front) is provided (no suitable list-based algorithm), why not mp_map_push_back|front)?
Our disagreement here rests on whether set should be the same as map. I do not view them the same at all. Set is a list with an added constraint, but it's still a list, and list-like things can be done to it. It's lower level, so order matters. Map is a higher-level key-based structure, where order doesn't matter (logically.)
Something more generic can be provided
struct mp_mpl_sequence_folder
It can, but it ties me to MPL, so I've chosen not to provide it.
Additionally:
* "A metafunction is a class template or a template alias [...]": s/"template alias"/"alias template"? * "mp_unique<L> returns a list of the same type as L [...]": being "the same type" needs definition. This is found in other places throughout the documentation.
You're right, the documentation is not particularly rigorous. I use C++ terms here and the colloquial meaning of "type" there. I tend to think that the meaning is more or less clear, but things could be tightened up at the expense of terseness.
We already have subnamespaces in std (e.g. std::chrono), so your standardization plan could be realized as s/boost/std.
It could if I wanted to propose std::mp::if_, but I don't. I want to propose std::mp_if.
I know that this makes the library more difficult to use in other Boost libraries where there's no convenient place to put the using directive. :-/
To expand on this a bit: There are, in general, two main modes of use of Mp11, serving two audiences. One is the "easy mode", where one includes <boost/mp11.hpp>, combines that with `using namespace boost::mp11;`, then goes ahead using mp_this and mp_that without qualification. This serves (a) people who play with metaprogramming in short test cases, (b) people who have a need for a metaprogram in a .cpp file (or an internal header file not meant for public consumption), whether library or application one. Mode two is in use in header-only libraries. There the library author is generally reluctant to employ the using directive, which forces the comparatively awkward mp11::mp_this style. I realize that my choice gives preference to case one at the expense of case two.
El 17/07/2017 a las 13:05, Peter Dimov via Boost escribió:
To expand on this a bit:
There are, in general, two main modes of use of Mp11, serving two audiences. One is the "easy mode", where one includes <boost/mp11.hpp>, combines that with `using namespace boost::mp11;`, then goes ahead using mp_this and mp_that without qualification. This serves (a) people who play with metaprogramming in short test cases, (b) people who have a need for a metaprogram in a .cpp file (or an internal header file not meant for public consumption), whether library or application one.
These people would be equally served by instructing them to namespace mp=boost::mp11; and then go ahead with mp::this and mp::that. Joaquín M López Muñoz
El 17/07/2017 a las 12:20, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
El 17/07/2017 a las 1:06, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
1.1 I really don't like the mp_ prefix. [...]
Well... it's also meant to make it possible to move mp11 into std > without changes. :-)
We already have subnamespaces in std (e.g. std::chrono), so your standardization plan could be realized as s/boost/std.
It could if I wanted to propose std::mp::if_, but I don't. I want to propose std::mp_if.
I know that this makes the library more difficult to use in other Boost libraries where there's no convenient place to put the using directive. :-/
As I see it, users of your library can be divided into three targets: 1. Regular users of Boost. 2. Boost authors (like myself). 3. Users of the std library, once this is proposed to the committee, accepted and implemented. My personal opinion is that 1 should be given priority, followed by 2 and, at a distance, 3. It is a submission for Boost we're dealing here with. In that light, mp_ sounds like noise to me (also for 3, but this is not a subject for this review).
[...]
Our disagreement here rests on whether set should be the same as map. I do not view them the same at all. Set is a list with an added constraint, but it's still a list, and list-like things can be done to it. It's lower level, so order matters. Map is a higher-level key-based structure, where order doesn't matter (logically.)
Totally valid point of view. My intuition about mp11::map comes from the definition ("a list of lists [...]") and the std::set/std::map analogy. If you want to pull readers away from that mindset you might want to be a little more descriptive about what a map is meant to be / be used for, and/or change the name to something not semantically overloaded like, say, "dictionary".
Something more generic can be provided
struct mp_mpl_sequence_folder
It can, but it ties me to MPL, so I've chosen not to provide it.
This can be confined to a dedicated header, so that the rest of Mp11 is MPL-free and your planned std proposal won't be polluted, and you'd be providing a service to potential users willing to migrate from Boost.MPL but wanting to do so in a progressive manner. Joaquín M López Muñoz
On 07/17/2017 01:06 PM, Joaquin M López Muñoz via Boost wrote:
My personal opinion is that 1 should be given priority, followed by 2 and, at a distance, 3. It is a submission for Boost we're dealing here with. In that light, mp_ sounds like noise to me (also for 3, but this is not a subject for this review).
The main problem is that some names would collide with C++ keywords. A quick scan of the documentation reveal these possible conflicts: bool, true, false, int, if, and void. I am wondering if there is an alternative solution to the mp_ prefix and the MPL-style _ suffix. For instance, type-traits have faced the same problem and selected synonyms, e.g. std::conditional instead of if, or longer names, e.g. true_type and false_type. Would that be a feasible approach?
El 22/07/2017 a las 11:23, Bjorn Reese via Boost escribió:
On 07/17/2017 01:06 PM, Joaquin M López Muñoz via Boost wrote:
My personal opinion is that 1 should be given priority, followed by 2 and, at a distance, 3. It is a submission for Boost we're dealing here with. In that light, mp_ sounds like noise to me (also for 3, but this is not a subject for this review).
The main problem is that some names would collide with C++ keywords. A quick scan of the documentation reveal these possible conflicts: bool, true, false, int, if, and void.
I am wondering if there is an alternative solution to the mp_ prefix and the MPL-style _ suffix.
Besides prefixing and suffixing, the only reamining alternative is using another name, of course.
For instance, type-traits have faced the same problem and selected synonyms, e.g. std::conditional instead of if, or longer names, e.g. true_type and false_type.
Personally, I like MPL-style suffixing better: it's a sort of tradition within Boost (boost::hana::and_, boost::proto::if_, boost::msm::front::euml::while_), name intent is clear and seems to me the _ suffix it's automatically parsed out by the (trained) human eye. Joaquín M López Muñoz
Joaquin M López Muñoz wrote:
Something more generic can be provided
struct mp_mpl_sequence_folder { template<typename T,typename L> struct apply{using type=mp_push_front<T,L>;}; };
template<typename MplSequence> struct mp_mpl_sequence_impl:boost::mpl::reverse_fold< MplSequence, mp_list<>, mp_mpl_sequence_folder
{};
template<typename MplSequence> using mp_mpl_sequence=typename mp_mpl_sequence_impl<MplSequence>::type;
How about we provide it in MPL instead? namespace mpl { template<class Seq, template<class...> class L = std::tuple> using to_tuple = /*as above*/ } to_tuple subject to bikeshedding (to_variadic? as_variadic_sequence?).
El 17/07/2017 a las 23:03, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
Something more generic can be provided
[...]
template<typename MplSequence> using mp_mpl_sequence=typename mp_mpl_sequence_impl<MplSequence>::type;
How about we provide it in MPL instead?
namespace mpl {
template<class Seq, template<class...> class L = std::tuple> using to_tuple = /*as above*/
}
to_tuple subject to bikeshedding (to_variadic? as_variadic_sequence?).
Well, I guess users don't care where the utility belongs as long as it's documented and available somewhere. If you want to integrate with MPL nicely, this probably should take the form of a variadic sequence inserter (http://www.boost.org/libs/mpl/doc/refmanual/inserter.html ). Something like: struct variadic_inserter_op { template<template<typename...> class L,typename... Ts,typename T> static L<Ts...,T> helper(L<Ts...>,T); template<typename L,typename T> struct apply { using type=decltype(helper(std::declval<L>(),std::declval<T>())); }; }; template<template <typename...> class L> struct variadic_inserter:boost::mpl::inserter<L<>,variadic_inserter_op>{}; template<class Seq,template<typename...> class L=std::tuple> using to_variadic_sequence=boost::mpl::copy<Seq,variadic_inserter<L>>; Note that this would be the only C++11 piece of code in Boost.MPL. Joaquín M López Muñoz
On Tue, Jul 18, 2017 at 9:58 AM, Joaquin M López Muñoz via Boost < boost@lists.boost.org> wrote:
El 17/07/2017 a las 23:03, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
Something more generic can be provided
[...]
template<typename MplSequence> using mp_mpl_sequence=typename mp_mpl_sequence_impl<MplSequen ce>::type;
How about we provide it in MPL instead?
namespace mpl {
template<class Seq, template<class...> class L = std::tuple> using to_tuple = /*as above*/
}
to_tuple subject to bikeshedding (to_variadic? as_variadic_sequence?).
Personally I think this doesn't belong to MPL. Actually I suggest providing a slightly more general utility that not only converts MPL Sequences, but Metafunction Classes as well and also in a recursive fashion, that is, Sequences of Sequences would be converted to variadic Lists of variadic Lists for example. Metal used to provide such a utility up until recently, when I decided to remove it, but maybe it can still serve as a source of inspiration [1]. Some usage examples are available as well [2]. [1]: https://github.com/brunocodutra/metal/blob/85d6d32f6e58e648c4573189f8bf2d763... [2]: https://github.com/brunocodutra/metal/blob/85d6d32f6e58e648c4573189f8bf2d763... Bruno
Bruno Dutra wrote:
How about we provide it in MPL instead?
namespace mpl {
template<class Seq, template<class...> class L = std::tuple> using to_tuple = /*as above*/
} ...
Personally I think this doesn't belong to MPL.
A cursory search seems to show that the desire to use std::tuple with MPL is not uncommon, although getting a tuple out of an MPL sequence is of course only half of the story, people also want to use std::tuple as if it were an MPL sequence.
Personally I think this doesn't belong to MPL.
A cursory search seems to show that the desire to use std::tuple with MPL is not uncommon, although getting a tuple out of an MPL sequence is of course only half of the story, people also want to use std::tuple as if it were an MPL sequence.
I've decided to provide the latter instead. That is, upon inclusion of <boost/mp11/mpl.hpp>, both mp_list and std::tuple become MPL sequences and can be manipulated with MPL algorithms. This implies the ability to convert an arbitrary MPL sequence into an mp_list or std::tuple, via mpl::copy<Seq, mpl::back_inserter<mp_list<>>>::type. https://github.com/pdimov/mp11/blob/feature/mpl/include/boost/mp11/mpl.hpp https://github.com/pdimov/mp11/blob/feature/mpl/test/mpl.cpp
El 18/07/2017 a las 15:51, Peter Dimov via Boost escribió:
Personally I think this doesn't belong to MPL.
A cursory search seems to show that the desire to use std::tuple with MPL is not uncommon, although getting a tuple out of an MPL sequence is of course only half of the story, people also want to use std::tuple as if it were an MPL sequence.
I've decided to provide the latter instead. That is, upon inclusion of <boost/mp11/mpl.hpp>, both mp_list and std::tuple become MPL sequences and can be manipulated with MPL algorithms.
This looks cool. Thank you! Joaquín M López Muñoz
On Mon, Jul 17, 2017 at 1:06 AM, Peter Dimov via Boost < boost@lists.boost.org> wrote:
Joaquin M López Muñoz wrote:
Porting from / coexisting with Boost.MPL would be greatly aided by some utility function to convert Boost.MPL sequences to Mp11 lists:
...
This unfortunately doesn't work in general - as Bruno Dutra explained to me - because MPL operations on, f.ex. mpl::vector, do not necessarily return an mpl::vector.
This reads backwards to me. It's true that the signature (i.e. types) of Boost.MPL containers are left unspecified and, indeed, on some platforms mpl::vector is implemented via inheritance through opaque proxy types, but that only means one can't simply rely on the property that any instance of a variadic template class is a List to advertise seamless interoperation with Boost.MPL, which is also aggravated by the fact it emulates variadic parameters packs via default template arguments. The alternative, which seems to be what Joaquin proposes here, is to provide a utility metafunction that explicitly converts from Boost.MPL containers into variadic Lists and one simple way of doing it is to use mpl::fold to push the elements of Boost.MPL containers one by one into a variadic List by means of a custom Metafunction Class in the Boost.MPL sense. Bruno
Joaquin M López Muñoz wrote:
1.7 mp_eval[_xx] functions should accept metafunctions in both their true and false branches.
I can't make this work. #include <type_traits> #include <tuple> template<bool C, template<class...> class F, class... T, template<class...> class G, class... U> using eval_if = void; int main() { using R = eval_if<true, std::tuple, int, float, std::tuple, void>; } prog.cc:4:55: error: template parameter pack must be the last template parameter template<bool C, template<class...> class F, class... T, template<class...> class G, class... U> using eval_if = void; ^ One might argue that it ought to work because G would obviously terminate the first pack, but it doesn't. :-) mp_eval_if_c<cond, mp_eval_if_c<!cond, void, F, T...>, G, U...> is not pretty but... it works. There's also the option of typename mp_if_c<cond, mp_defer<F, T...>, mp_defer<G, U...>>::type.
El 17/07/2017 a las 1:24, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
1.7 mp_eval[_xx] functions should accept metafunctions in both their true and false branches.
I can't make this work. [...]
This is most unfortunate. My original concern remains though that there's no reason why one of the two branches of mp_eval_if should not accept a metafunction (and note the irony that mp_eval_if<B,...> precisely does not eval when B holds). If syntax can't be made to work, it could be advisable to drop mp_eval_if altogether and provide for instance mp_apply_if: mp_apply_if<B,F,mp_list<T...>,G,mp_list<U...>> Joaquín M López Muñoz
Joaquin M López Muñoz wrote:
If syntax can't be made to work, it could be advisable to drop mp_eval_if altogether and provide for instance mp_apply_if:
mp_apply_if<B,F,mp_list<T...>,G,mp_list<U...>>
Given that the one function formulation covers a significant majority of the practical use cases, dropping it in favor of mp_apply_if would be a significant regression in usability.
https://github.com/joaquintides/multi_index_mp11/commit/91f2b32cf5a27b3aeaaf...
template<typename TagList> struct no_duplicate_tags { typedef mp11::mp_fold< TagList, mp11::mp_list<>, mp11::mp_set_push_back > aux;
This is mp_unique<TagList>, but...
BOOST_STATIC_CONSTANT( bool,value=(mp11::mp_size<TagList>::value==mp11::mp_size<aux>::value)); };
... this in its entirety is what Metal calls "distinct", the counterpart to mp_same<T...> that returns true not when all Ts are the same, but when all Ts are distinct. A good argument for adding mp_distinct (or the equivalent list-based formulation mp_is_set), I suppose.
Joaquin M López Muñoz wrote:
1.4 Tuple operations are named differently from their C++14/17 counterparts to "avoid ambiguities when both are visible or in unqualified calls". Yet, this policy is not followed in integer_sequence.hpp. I'd rather go the latter way.
The primitives in integer_sequence.hpp are template aliases, so argument-dependent lookup does not apply to them. The tuple functions are, well, functions, and when f.ex. make_from_tuple<T>(tp) is called with an std::tuple, ADL finds std::make_from_tuple because std is an associated namespace. So the code suddenly becomes ambiguous in C++17.
El 17/07/2017 a las 3:11, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
1.4 Tuple operations are named differently from their C++14/17 counterparts to "avoid ambiguities when both are visible or in unqualified calls". Yet, this policy is not followed in integer_sequence.hpp. I'd rather go the latter way.
The primitives in integer_sequence.hpp are template aliases, so argument-dependent lookup does not apply to them. The tuple functions are, well, functions, and when f.ex. make_from_tuple<T>(tp) is called with an std::tuple, ADL finds std::make_from_tuple because std is an associated namespace. So the code suddenly becomes ambiguous in C++17.
You can replace your implementation with using std::make_from_tuple when the latter is available. After all, the perceived intention of these functions is precisely to serve as a substitute in wait for C++17 to arrive. Same goes for integer_sequence.hpp, even if the conditions for collision are different. Joaquín M López Muñoz
El 17/07/2017 a las 11:54, Peter Dimov via Boost escribió:
Joaquin M López Muñoz wrote:
You can replace your implementation with using std::make_from_tuple when the latter is available.
You can still replace construct_from_tuple, you're just not forced to because the code doesn't break.
Not totally sure I got my point across: I mean, if you decided to rename construct_from_tuple as make_from_tuple, your internal lib code can detect whether std::make_from_tuple exists and, in this case, replace boost:mp11::make_from_tuple definition with a using std::make_from_tuple declaration: namespace boost{ namespace mp11{ #ifndef __cpp_lib_make_from_tuple template<class T, class Tp> T make_from_tuple(Tp&& tp){...} #else using std::make_from_tuple; #endif }} No ambiguities would then arise. Joaquín M López Muñoz
Hi,
This said, we can also look out to the wider world and recognize that Mp11 and Brigand have the very same design philosophy and come up with solutions that are exactly interchangeable. It would be great if we could find a way that efforts behind Mp11 and Brigand could be coordinated to come up with something bigger that could serve as Boost's pure-type metaprogramming library as >well as a non-Boost lib. To be honest, I don't have a clue how this could be done, or whether there's willingness to collaborate among Mp11's and Brigand's authors.
Brigand has been inspired by Peter Dimov's original article which explains the similarities. There is currently redesign work going on in Brigand. Odin is considering bringing Kvasir magic into Brigand for more speed, which explains why we have been very quiet. We are very willing to collaborate, we are just busy improving the library (and our lives also :-) ). Peter is most welcomed to take anything he likes from Brigand and incorporate it into Mp11, and I'm available by mail or on cppchat to discuss things if need be. Kind regards. -Edouard
Before I write a review, I have a couple of questions that are the outgrowth of having been using mp11 since some time after the first two articles of Peter’s. That should suggest I generally like the library, which I do. Nevertheless, in my code, I have found need for the following additional metafunctions: - mp_map_keys: creates a list of keys from a map - mp_is_map: type trait to determine whether a type is a map, i.e., has a unique set of keys - mp_is_set: type trait to determine whether a type is a set, i.e., has unique elements Is there a reason that support for such functions should not be in the library? I have also found need for a metafunction that takes a map and a set of keys and returns a map with only elements with the selected keys. This is perhaps more specialized, but I can also see a general use case. I would appreciate your thoughts on these functions. Thanks. Cheers, Brook
Brook Milligan wrote:
Before I write a review, I have a couple of questions that are the outgrowth of having been using mp11 since some time after the first two articles of Peter’s. That should suggest I generally like the library, which I do.
Nevertheless, in my code, I have found need for the following additional metafunctions:
- mp_map_keys: creates a list of keys from a map
With metaprogramming libraries, it's always quite difficult to decide what to include and what to leave out, as there are so many useful things that are also one-liners. As everyone has probably surmised by now, I prefer minimalism, that is, omit by default, only include when a clear need arises. mp_map_keys, for example, is mp_transform<mp_first, M>.
- mp_is_set: type trait to determine whether a type is a set, i.e., has unique elements
This at the moment is std::is_same<S, mp_unique<S>>, although as I mentioned, perhaps either mp_is_set or mp_distinct should indeed be provided.
- mp_is_map: type trait to determine whether a type is a map, i.e., has a unique set of keys
This is mp_is_set<mp_map_keys<M>>, although not quite if M has an element that is not a list.
I have also found need for a metafunction that takes a map and a set of keys and returns a map with only elements with the selected keys. This is perhaps more specialized, but I can also see a general use case.
template<class M, class L> using mp_map_subset = mp_copy_if<M, mp_bind<mp_set_contains, L, mp_bind<mp_first, _1>>::template fn>; mp_copy_if_q, when I add it, will eliminate the need for the ::template fn here.
On Jul 16, 2017, at 7:00 PM, Peter Dimov via Boost <boost@lists.boost.org> wrote:
Brook Milligan wrote:
Before I write a review, I have a couple of questions that are the outgrowth of having been using mp11 since some time after the first two articles of Peter’s. That should suggest I generally like the library, which I do.
Nevertheless, in my code, I have found need for the following additional metafunctions:
- mp_map_keys: creates a list of keys from a map
With metaprogramming libraries, it's always quite difficult to decide what to include and what to leave out, as there are so many useful things that are also one-liners. As everyone has probably surmised by now, I prefer minimalism, that is, omit by default, only include when a clear need arises.
Fair enough. The challenge is figuring out when a clear need actually does arise. I raised this set in part to see if anyone else speaks up to suggest that they are needed.
mp_map_keys, for example, is mp_transform<mp_first, M>.
Yes, that was my implementation.
- mp_is_set: type trait to determine whether a type is a set, i.e., has unique elements
This at the moment is std::is_same<S, mp_unique<S>>, although as I mentioned, perhaps either mp_is_set or mp_distinct should indeed be provided.
Yes, that was my implementation, too. It does feel like this is worth including as mp_distinct, but your call.
- mp_is_map: type trait to determine whether a type is a map, i.e., has a unique set of keys
This is mp_is_set<mp_map_keys<M>>, although not quite if M has an element that is not a list.
Yes, that was also my implementation. I just punted on the question of whether or not mp_map_keys would always work. Given that it might not, this could be motivation for a more robust implementation in the library.
I have also found need for a metafunction that takes a map and a set of keys and returns a map with only elements with the selected keys. This is perhaps more specialized, but I can also see a general use case.
template<class M, class L> using mp_map_subset = mp_copy_if<M, mp_bind<mp_set_contains, L, mp_bind<mp_first, _1>>::template fn>;
mp_copy_if_q, when I add it, will eliminate the need for the ::template fn here.
My implementation was a bit more complicated, because I didn’t quite get how to use mp_bind correctly. Perhaps more than suggesting that this be implemented, this suggests more complete documentation on bind. At least to me, this is considerably more complex than some of the simpler metafunctions and therefore warrants more documentation, including examples. This is likely true for many of the algorithms. Cheers, Brook
Brook Milligan wrote:
- mp_is_map: type trait to determine whether a type is a map, i.e., has a unique set of keys
This is mp_is_set<mp_map_keys<M>>, although not quite if M has an element that is not a list.
Yes, that was also my implementation. I just punted on the question of whether or not mp_map_keys would always work. Given that it might not, this could be motivation for a more robust implementation in the library.
This also raises the question of what mp_is_set should do when passed something that is not a list - return false or cause a substitution failure (which in turn hints at the lack of mp_is_list as well). Thank you for you suggestions. They make sense and I'll probably add them.
Brook Milligan wrote:
Nevertheless, in my code, I have found need for the following additional metafunctions:
- mp_map_keys: creates a list of keys from a map
https://github.com/pdimov/mp11/commit/50d35a29644bfb051316ae9a21f2f7b6dbe918...
- mp_is_map: type trait to determine whether a type is a map, i.e., has a unique set of keys
https://github.com/pdimov/mp11/commit/34946d2ae25a1e113c23c6ead3c55224695a7b...
- mp_is_set: type trait to determine whether a type is a set, i.e., has unique elements
https://github.com/pdimov/mp11/commit/27d0d547ee4d0cb83b678b176c8e96cb78de19...
I am interested in MP11 and will submit a review before the end of the review period. I want to make some comments in advance of this on some topics which I have not seen discussed already. These particularly relate to mp_quote_trait which I am finding very useful to adapt old C++03 code. In my initial work using MP11 I found that the examples given in the documentation contained some features which are not C++11 but instead C++14 or later. As I am wanting to adapt code from C++03 it is not helpful to have things which do not compile with C++11 only. I am particularly interested in code which works out return types for functions where the application can be polymorphic. The origins of much of this code are quite old now, originating with FC++ although there are more modern versions in Boost Phoenix. See for example http://www.boost.org/doc/libs/1_64_0/libs/phoenix/doc/html/phoenix/lazy_list... One item in MP11 which I have found useful is mp_quote_trait. This seems to be a late addition to the code as it was not in the first version of the code I downloaded. One thing which was not clear to me in the documentation was how to use the resulting object. I looked for things with “q” in the name. The answer to this is mp_invoke which does not have a “q”. Example: using mp_q_common_type_t = mp_quote_trait<std::common_type>; template <class... T> using result_q_t = mp_invoke<mp_q_common_type_t, T...>; This is very helpful as the resulting code works with C++11 avoiding the need to define common_type_t which is only introduced in C++14. I have also used the following extensions to MP11 invented to handle case where the struct to be quoted has int N at start o the list of types. template<template<int, class...> class F> struct mp_quote_trait_N { template<int N, class... TN> using fn = typename F<N, TN...>::type; }; template<class Q, int N, class... TN> using mp_invoke_N = typename Q::template fn<N,TN...>; One application if this is the specialization the result type from a function FF with different numbers of arguments. using mp_q_result_of_t = mp_quote_trait<boost::result_of>; template <int N, typename FF, typename... XYZ> struct RTHelper; template <typename FF,typename A> struct RTHelper <1,FF,A> { typedef mp_invoke<mp_q_result_of_t,F(A)> type; }; and for N=2, 3 etc. I hope this is helpful Best wishes John Fletcher
Fletcher, John P wrote:
In my initial work using MP11 I found that the examples given in the documentation contained some features which are not C++11 but instead C++14 or later. As I am wanting to adapt code from C++03 it is not helpful to have things which do not compile with C++11 only.
I went over these at one point and tried to either remove the C++14-isms, or mention their use and provide a C++11 alternative.
________________________________________ From: Boost [boost-bounces@lists.boost.org] on behalf of Peter Dimov via Boost [boost@lists.boost.org] Sent: 17 July 2017 16:20 To: boost@lists.boost.org Cc: Peter Dimov Subject: Re: [boost] [review][mp11] Formal review of Mp11
Fletcher, John P wrote:
In my initial work using MP11 I found that the examples given in the documentation contained some features which are not C++11 but instead C++14 or later. As I am wanting to adapt code from C++03 it is not helpful to have things which do not compile with C++11 only.
I went over these at one point and tried to either remove the C++14-isms, or mention their use and provide a C++11 alternative.
Thank you for that - it has made things clear to me now. John _______________________________________________ Unsubscribe & other changes: https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Flists.boost.org%2Fmailman%2Flistinfo.cgi%2Fboost&data=02%7C01%7CJ.P.Fletcher%40aston.ac.uk%7C350ec137efc54797930c08d4cd275bdd%7Ca085950c4c2544d5945ab852fa44a221%7C0%7C0%7C636359016309074831&sdata=tfC%2BwHzgIHaV%2BISUtc44s10g9KVuUdKtyXc3vq%2F7kog%3D&reserved=0
Here is my review of MP11. 1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity. Yes I think it should be accepted. There are changes I would like to see to the documentation. I don’t regard those as setting a condition on acceptance. 2. What is your evaluation of the design? I like the design which is consistent and clear. I have found it easy to understand. 3. What is your evaluation of the implementation? The implementation is also clear and consistent. I have found it easy to understand and to develop extensions to cover cases not in the implementation. 4. What is your evaluation of the documentation? I have had some problems with the documentation. Some of these came up when I attempted to use the examples and found that not all of the example code was C++11 compatible. I found ways around this and I believe that this has now been sorted out. Some items e.g. mp_identity_t<T> and mp_inherit<T…> have no documentation other that a code line and no example. I think there is a need for more examples in some areas. 5. What is your evaluation of the potential usefulness of the library? I think there is a great potential for this. I particularly like mp_quote_trait<F> and mp_invoke<Q,T…> which have many uses to simplify the implementation of type deduction, particularly of return types from functions. This is an area which has not been discussed much in the course of the review. 6. Did you try to use the library? With what compiler? Did you have any problems? I have used the library mainly with Clang 4.0 and also with gcc 4.9.2 working on Linux. My initial problems were getting some examples to work with C++11. This is mainly the extra things which are in std for C++14. I overcame these in most cases using mp_quote_trait<F> which gave code which works with C++11 and C++14. I have gone on to reimplement some code which does fairly complicated operations to get return types. I used mp_quote_trait<F> and mp_invoke<Q,T…> and removed all of the ::type code from my new code. I have also written extensions to mp_quote_trait to allow for the case where the inner function is of the form F<int N,T…>. I wanted this to allow F to be called with the contents of a list L<T…> for cases N = 1,2,3 corresponding to the number of arguments of F. For this it is necessary to call specialisations of a helper function. The code is like this, where I have defined mp_inside_N to allow access to T… // Extensions to MP11 invented to handle case with int N at start of types. template<template<int, class...> class F> struct mp_quote_trait_N { template<int N, class... T> using fn = typename F<N, T...>::type; }; template<class Q, int N, class... TN> using mp_invoke_N = typename Q::template fn<N,TN...>; // mp_inside_N allows operations to work on the pack inside the list. // This applies mp_invoke_N and is the case I need here. template <class Q,class FF,class LL> struct mp_inside_N; template <class Q,class FF, template<class...> class L, class... TL> struct mp_inside_N <Q,FF,L<TL...> > { typedef mp_invoke_N<Q,sizeof...(TL),FF,TL...> type; }; using mp_q_inside_N_t = mp_quote_trait<mp_inside_N>; // usage, where RTHelper is the helper struct e.g. template <int N, typename FF, typename... XYZ> struct RTHelper; template <typename FF,typename A> struct RTHelper <1,FF,A> { typedef mp_invoke<mp_q_result_of_t,FF(A)> type; }; // and for N=2,3 etc as needed. using mp_q_RTHelper_t = mp_quote_trait_N<RTHelper>; typedef mp_invoke<mp_q_inside_N_t,mp_q_RTHelper_t,FType,ArgTypes> ReturnType; I have not used MP11 yet to write new code which I did not have before. 7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? I have worked for some hours for several days getting an understanding and implementing examples. 8. Are you knowledgeable about the problem domain? I have worked for a period of years on templated code, from FC++ to Boost Phoenix. I have written some variadic codes. I am not so knowledgeable about C++14. I have explored other new libraries including Hana. I appreciate this code and expect to go on using it. Thank you to Peter for contributing it. John Fletcher
Fletcher, John P wrote:
Here is my review of MP11.
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity. Yes I think it should be accepted. There are changes I would like to see to the documentation. I don’t regard those as setting a condition on acceptance.
Thank you John.
Here is my formal review of Mp11.
On Jul 15, 2017, at 4:19 AM, Bjorn Reese via Boost <boost@lists.boost.org> wrote:
Mp11 is a C++11 metaprogramming library for compile-time manipulation of data structures that contain types. It’s based on template aliases and variadic templates and implements the approach outlined in the article "Simple C++ metaprogramming" [2] and its sequel [3]. These articles are useful background information for the review.
Background: at the time I first ran across these articles, I had been a reasonably proficient Boost.Mpl programmer, but was facing challenges and wanting to take advantage of c++11. I found these articles very helpful, copied a bunch of the code to explore, and learned quite a bit about metaprogramming in c++11. Thus, the foundation for Mp11 that Peter established early on was very helpful. Since then, I incorporated my copied and modified versions of the article code into a large project that needed some fairly tricky metaprogramming, and found Peter’s library very useful. Most recently, I have stripped out most of that code and made the Mp11 library an external dependency. That transition went perfectly smoothly and left only a handful of “custom” metafunctions built on top of Mp11. During the course of this review, Peter has already incorporated most of those in the library, because he agreed that they would be generally useful.
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicitly.
I feel Mp11 should definitely be accepted.
2. What is your evaluation of the design?
As mentioned above, the clarity of the design became evident to me early on after reading the original articles. At the time I was not a great c++11 programmer, but working with the early Mp11 code and extending it based upon the principles outlined in the articles was quite straightforward and taught me some useful things about c++11. I feel that the design has only improved since.
3. What is your evaluation of the implementation?
I have not spent much time with the current implementation. However, I did pour over the initial implementation to understand it and to extend it to cover a few additional use cases that I needed. I felt that the implementation made a lot of sense, especially in concert with the articles, which explained the “magic” really well.
4. What is your evaluation of the documentation?
The current documentation seems quite good, if a bit telegraphic at points. Keeping the links to the original articles is essential, and perhaps an abridged version of the most salient points would improve the introduction. It is hard to tell, because I read those articles long ago, learned their content, and have been using the library (or its precursor code) for quite awhile. There are, however, a few points that I have found difficult and about which the documentation is probably too telegraphic. While most of the metafunctions are quite simple, i.e., take one or two arguments and return an obvious type that is easy to reason about, I have more difficulty with the metafunctions that are composable. For example, using algorithms together with bind and friends is something that I feel greater clarity, including examples, would help. During the course of this review, I suggested a few metafunctions that I had found useful in my own code base that were simple extensions of Mp11. In all but the most complicated case, my implementation of the extension was exactly what Peter subsequently implemented in the library, having taken my suggestions to be useful enough for general consumption. I appreciate his adding to the library; more importantly, though, I feel that my ability to implement these metafunctions exactly like the pro, based only on the documentation available, is a strong indication that the document has served its purpose. At the same time, I implemented the final example (which involved an algorithm and bind) in a much more complicated way than Peter did; this too is an indication of my point above that the documentation on composing algorithms/bind/etc. into more complicated constructs can be improved.
5. What is your evaluation of the potential usefulness of the library?
I have been using this library (or its precursor) for the last year in a large code base. Thus, for me personally, it has already been very useful. I expect that it will be highly useful in general.
6. Did you try to use the library? With what compiler? Did you have any problems?
I have been using clang to work with the code base that uses Mp11.
7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I have not put a great deal of extra effort into this specific review. However, I have been using the library for a year, during which time I have thoroughly studied the original articles, copied the code itself, and modified bits to support metafunctions that were not originally provided. This work has been spread out over months, but I feel it corresponds to an in-depth study at some level.
8. Are you knowledgeable about the problem domain?
I am familiar with metaprogramming in general and use it extensively in my own code base. I had earlier relied on Boost.MPL, but largely as a result of Peter’s articles and my experience with Mp11 have switched entirely to using c++11 style metaprogramming to take advantage of variadics, type aliases, etc. Thus, I feel quite comfortable with this domain. Thanks to Peter for putting together such a carefully crafted library. Cheers, Brook
On Wed, Jul 19, 2017 at 12:47 PM, Brook Milligan via Boost < boost@lists.boost.org> wrote:
Here is my formal review of Mp11.
[snip]
5. What is your evaluation of the potential usefulness of the library?
I have been using this library (or its precursor) for the last year in a large code base. Thus, for me personally, it has already been very useful. I expect that it will be highly useful in general.
6. Did you try to use the library? With what compiler? Did you have any problems?
I have been using clang to work with the code base that uses Mp11.
7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I have not put a great deal of extra effort into this specific review. However, I have been using the library for a year, during which time I have thoroughly studied the original articles, copied the code itself, and modified bits to support metafunctions that were not originally provided. This work has been spread out over months, but I feel it corresponds to an in-depth study at some level.
This is one of the things with which I'm struggling when considering the use of mp11, or Brigand, or any of the other recent offerings out there. I've found TMP so *easy* in 11 and later, that I've entirely stop using a library to do it. I'm trying to understand why anyone would -- that's not my making an argument, just failure of imagination. :) Brook, could you share some example uses? Is mp11 more useful to you because it does something that would be hard for you to get right, or because it does lots of simple things that you'd have to repeat endlessly? Or perhaps it's something else? Zach
On Jul 19, 2017, at 12:41 PM, Zach Laine <whatwasthataddress@gmail.com> wrote:
I've found TMP so *easy* in 11 and later, that I've entirely stop using a library to do it. I'm trying to understand why anyone would -- that's not my making an argument, just failure of imagination. :)
It is true, that once learned type aliases, variadics, and c++11 make a _huge_ difference in one’s (at least my) ability to simplify metaprogramming. Thus, a consequence is exactly what you say, lots of individual, one-off little type aliases and such. In that sense, metaprogramming at one level is now similar to writing lambdas and such. However, see below ...
Brook, could you share some example uses? Is mp11 more useful to you because it does something that would be hard for you to get right, or because it does lots of simple things that you'd have to repeat endlessly? Or perhaps it's something else?
Sure. One application that I have found for a more complicated library like Mp11 is the following. Consider implementing a variadic function like the following: template < typename … Parameters > auto f (Parameters&& … parameters) { return detail::f (make_parameter_pack (make_parameter<tag::first>().default_value(0), make_parameter<tag::second>().default_value(1), make_parameter<tag::third>().default_value(2)), std::forward<Parameters>(parameters)...); } The idea is for detail::f() to take an object that has a variety of constraints (tags, default values, perhaps types) and an arbitrary set of arguments and glue them together into a resulting object that obeys the specified constraints. Sorting out the set of parameters and matching them against the constraints requires manipulation of lists, sets, and maps of the relevant types. I found that Mp11 made this fairly straightforward. I would not have wanted to build the infrastructure to represent and manipulate those data structures myself. I think this goes well beyond the simple type aliases that I think you are referring to above, but perhaps I misunderstand. Another application I have found is to allow customization of a set of possible type tags that a library will support. Using lists of valid tags against which user code can be validated helps support the following: (i) customization of a library to avoid including unwanted code, and (ii) validating user code against the current configuration of the library. Again, this involves manipulating metaprogramming data structures, not simply evaluating metafunctions. So, to me the value of something like Mp11 is that it provides some basic metaprogramming data structures, i.e., list, set, and map, along with the generic means of manipulating them. I think that is quite different from the “easy” metaprogramming you may be referring to. On the other hand, perhaps metaprogramming is much more transparent to you than to me, and you feel that managing a map in a nice generic way is “easy”. Cheers, Brook
Brook Milligan wrote:
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicitly.
I feel Mp11 should definitely be accepted.
Thanks Brook. Zach Laine wrote:
This is one of the things with which I'm struggling when considering the use of mp11, or Brigand, or any of the other recent offerings out there.
I've found TMP so *easy* in 11 and later, that I've entirely stop using a library to do it. I'm trying to understand why anyone would -- that's not my making an argument, just failure of imagination. :)
It gets really easy in 17 and later, as you no longer need mp_all because of fold expressions and there are all kinds of ad-hoc primitives in the standard library so you can get by. So for instance if you have a pack T... you can check if all those types are const (toy example) by doing (std::is_const_v<T> && ...), instead of mp_all<std::is_const_t<T>...> or gathering the types into an mp_list<T...> L and then using mp_all_of<L, std::is_const_t>. But even then, mp_unique or mp_remove(_if) are no fun to write by hand. In short, if what you want to do is not a simple pack expansion, you probably want to use the library. That, or reimplement it. :-)
Boost - Dev mailing list wrote
The formal review of Peter Dimov's Mp11 library is scheduled for July 15 - July 24, 2017 [1].
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity.
Yes. Especially so if mp11 can be merged with the ideas from kvasir and brigand.
2. What is your evaluation of the design?
I like the design, it is elegant and easy to understand.
3. What is your evaluation of the implementation?
It is solid. I would like to see some improvement in the sort algorithm, however.
4. What is your evaluation of the documentation?
The documentation is well-written. Easy to read, making it easy to find the stuff you need.
5. What is your evaluation of the potential usefulness of the library?
It would be very useful to me. I currently use the brigand library and find that library also very good. But if mp11 is acepted into boost, I will switch.
6. Did you try to use the library? With what compiler? Did you have any problems?
No, due to time pressure, I did not have time to use the library.
7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
A quick reading this time. I have been following Peter Dimovs work for some years though, beginning with his introduction to template metaprogramming which was an eye-opener to me.
8. Are you knowledgeable about the problem domain?
I am somewhat knowledgeable as I have implemented a template-library (much more limited in scope) and thus have lots of knowledge about how not to do template meta programming. ;-) I am also a user of brigand, and use it a lot - as my current project is (ab)using templates all over the place. /Peter -- View this message in context: http://boost.2283326.n4.nabble.com/review-mp11-Formal-review-of-Mp11-tp46968... Sent from the Boost - Dev mailing list archive at Nabble.com.
peterkochlarsen wriote:
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity.
Yes. Especially so if mp11 can be merged with the ideas from kvasir and brigand.
Thanks.
3. What is your evaluation of the implementation?
It is solid. I would like to see some improvement in the sort algorithm, however.
What specifically? It holds its own pretty well on metaben.ch, on Clang 4 it loses only to kvasir (but then again, who doesn't lose to kvasir?)
On Sat, Jul 22, 2017 at 9:10 PM, Peter Dimov via Boost <boost@lists.boost.org> wrote:
peterkochlarsen wriote:
1. Should Mp11 be accepted into Boost? Please state all conditions > for acceptance explicity.
Yes. Especially so if mp11 can be merged with the ideas from kvasir and brigand.
Thanks.
3. What is your evaluation of the implementation?
It is solid. I would like to see some improvement in the sort algorithm, however.
What specifically? It holds its own pretty well on metaben.ch, on Clang 4 it loses only to kvasir (but then again, who doesn't lose to kvasir?)
Sorry about the noise. As part of my review I went through the benchmarks at metaben.ch. I saw that sort performed badly for mp11, but revisiting the site I realise that I misread the graph. /Peter
1. Should Mp11 be accepted into Boost?
Yes.
2. What is your evaluation of the design?
It appears to be robust. I'm not sure about the design decision mp_ prefix for everything.
What is your evaluation of the implementation?
I haven't looked at it in detail.
4. What is your evaluation of the documentation?
Good. Very good.
5. What is your evaluation of the potential usefulness of the library?
6. Did you try to use the library? With what compiler? Did you have any
Quite high; should replace a lot of current boost MPL usage. problems? Only cursorily. With g++ 7.1 and clang++ 4.0. Did not encounter any problems.
8. Are you knowledgeable about the problem domain?
Somewhat. To the extent that I've used boost MPL quite a lot. On 15 July 2017 at 15:49, Bjorn Reese via Boost-announce < boost-announce@lists.boost.org> wrote:
The formal review of Peter Dimov's Mp11 library is scheduled for July 15 - July 24, 2017 [1].
Mp11 is a C++11 metaprogramming library for compile-time manipulation of data structures that contain types. It’s based on template aliases and variadic templates and implements the approach outlined in the article "Simple C++ metaprogramming" [2] and its sequel [3]. These articles are useful background information for the review.
* Mp11 aims to make simple usage simple, and to support complex usage without complicating the simple use cases.
* Mp11 works on any type-list, whether its own type-list mp_list, or standard type-lists such as std::tuple and std::variant, or user-defined type-lists.
* Mp11 works with any meta-function, such as C++11 or Boost type-traits, or user-defined type-traits.
Mp11 can be found here:
* Documentation: https://rawgit.com/pdimov/mp11 /master/doc/html/mp11.html
* Source code: https://github.com/pdimov/mp11/tree/master
Please answer the following questions in your review [4]:
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity.
2. What is your evaluation of the design?
3. What is your evaluation of the implementation?
4. What is your evaluation of the documentation?
5. What is your evaluation of the potential usefulness of the library?
6. Did you try to use the library? With what compiler? Did you have any problems?
7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
8. Are you knowledgeable about the problem domain?
[1] http://www.boost.org/community/review_schedule.html [2] http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html [3] http://pdimov.com/cpp2/simple_cxx11_metaprogramming_2.html [4] http://www.boost.org/community/reviews.html _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman /listinfo.cgi/boost-announce
On 7/15/17 3:19 AM, Bjorn Reese via Boost wrote: Here is my review.
Please answer the following questions in your review [4]:
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity. YES
2. What is your evaluation of the design? Incredible
3. What is your evaluation of the implementation? fantastic
4. What is your evaluation of the documentation? excellent - but see notes below
5. What is your evaluation of the potential usefulness of the library? beyond believe
6. Did you try to use the library? With what compiler? Did you have any problems? no. Actually I was unclear whether I need C++11 or C++14
7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? Not much - Spend maybe 2 hours trying to figure out the documentation. Not enough time - but I feel I have a fairly good understanding of
8. Are you knowledgeable about the problem domain? More than average, but not as much as some
Observations: Much has been said about the mp_ prefix. I'm not crazy about it as I would much prefer to use mp11::transform rather than mp11::mp_transform. But I'm not really bothered by it all that much. It seems that Peter has made a case for it and it's being discussed. I don't much about this so I'm happy with whatever is finally used. Calling this a library does not do it justice. It's nothing less than the text book of how C++11+ should/can be used. I would like to see even more examples and expository examples. It should really be a book - sequel to Abrahams and Gurtovoy's book which itself was sequel to Alexandrescu's book. I started reading the documents and kept these notes. I quit when I ran out of gas. ============== Incorporation of Simple C++ meta programming and sequel int to the documentation directly rather than by reference. I'm guessing this might be part of a section - "how the library is implemented". I would also like to see a section which describes the parallels with boost mpl. I see this is a newer, better, simpler, faster, more modern, etc. replacement for mpl. Assuming I'm correct I would like to see this described in a separate section titled something like - background/history or something similar. Definitions A "quoted metafunction" is a class with a public metafunction member called fn, ... I presume that this is the same as the mpl notion of metafunction class which must contain a public metafunction member called "apply" A "map" is a list of lists, the inner lists having at least one element (the key.) The keys of the map must be unique. From the examples, I can't discern what the key is. Is it the first element of each set? or ... Generating test cases This is a great motivating example. For the safe numerics library I used the Boost.Preprocessor library for this purpose. Had this existed at the time, I would have turned to it first. It's a use case which is useful in the real world. It's unclear whether one needs C++11 or C++14 in order to use this library. Writing common_type Specializations This example is more complicated and I'm having a hard time understanding it. This is not a criticism - I just think I have invest more effort. I'm motivated as I believe it has some relevence to some stuff I'm currently working on. But I'm having trouble with the example: I seems that mp_defer is related if not equivalent to the mpl notion of "eval_if". I'm not clear on this since mp_defer isn't really describe The example also reference mp_transform whose purpose is not obvious. One thing that might be making it difficult for me is the usage of common_type_t for the example metafunction name which uses std::common_type in it's implementation. Then there is common_tuple. Perhaps altering the names might make all this more understandable to the normal brain. Soooo - consider: a) thinking some more about the names b) inserting comments into the example code c) perhaps reformation the code somewhat keep the comments in a good place d) including information - a cheat sheet which relates notions in this library to notions/nomenclature in boost.mpl. Or consider using the MPL nomenclature. This latter idea is intersting as this is clearly meant to be the "modern" version of MPL. e) thie common_type_t is just an alias for std::common_type. I'm not convinced it clarifies anything. With all that the example might look more like: "Let’s write a common_type specialization for two std::tuple arguments which we'll call common_tuple." Hmmm - what is this going to return? It sounds like that it's going to produce a new tuple where the first element is the common_type of first element of each constituent tuple, the second is the common_type of second tuple and so forth. But that's just a guess though create a tuple from template<class Tp1, class Tp2> using common_tuple = // mp_transform does ... what - returns a tuple (li mp_transform< std::common_type, // from standard C++14 Tp1, // first tuple ? Tp2 // second tuple ? >; Then specialize std::common_type to use the above ... OK have written the above it might be making sense to me now. My basic point is that these examples need a lot of extra care as they are essential to explaining what the library is for an how to use it to most programmers. I've worked many years with MPL and still I have difficulty understanding all this. Looking back I see that mp_transform IS described in the overview. I didn't pay much attention to the overview as reading something labeled "overview" is for wimps. Now that I look back on it I see that I should have read it. The first sentence exactly and succinctly describes what the library does. I think this sentence should have it's own paragraph. This library is not really a library. Its an unlibrary. "As another example, consider the hypothetical type expected<T, E…>" Another excellent example. Although its related to the previous one, it should have it's own heading. It's particularly interesting given all the thunder and lightening that this topic has recently received. "Fixing tuple cat" This example would require many hours and referring to another paper to figure out. I don't think it's suitable as an introduction to the library. "Computing Return types" Another good example. From the first paragraph I understood the problem and the proposed solution using variant. Given that I'm already getting burned out from reading this stuff, I decided to declare victory and move on. "Reference" I absolutely loved having the table of contents in the left hand window of the documentation. I fund it very, very, very helpful and intuitive. I do question the fact that reference section includes just a couple of lines for each library function. I much prefer and learned a lot from the boost mpl documentation which includes a small compilable example for each function. Maybe you might lift those examples and include them here. A lot of work I know, but that's why you get paid the big bucks. Actually I would prefer for web documentation that each metafunction have it's own page, with type requirements for the parameters, and small self contained examples. This is not really a library, it's an exposition how to use of C++11++. It's a way of training the next generation of programmers to use TMP. They will be standing on your shoulders by means of this document. I think you way underestimate this document and library will have on the future. =========== Robert Ramey
Hi, my review ( this is my first one ): 1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicitly. Yes. No conditions. 2. What is your evaluation of the design? I don't know the other MP libraries, so I lack some basis of comparison But anyway, the library is easy to use an has vast application. So I find the design very good. I have nothing against the mp_ prefix, There are just some names I would change. Nothing really important. Just matter of personal taste. But will mention them to see if others agree: mp_eval_if_c<C, T, F, U…> From this name I would expect F<U...> to be evaluated when C is true, instead of when it's false. mp_take The name doesn't make it clear what it is taking. I would rather call it mp_front_slice, or something like that. 3. What is your evaluation of the implementation? Very good: has good performance, well organized, easy to understand and to maintain. 4. What is your evaluation of the documentation? I found the reference easy to follow. The examples required some effort. But only because they really exercise my understanding. And that's a good thing, IMO. TMP requires from the newcomers to think in a different way and the two preliminary articles are really helpful in this sense. I found them very valuable. They made me fell stimulated instead of intimidated. 5. What is your evaluation of the potential usefulness of the library? Some claimed that modern C++ makes TMP so much easier that TMP libraries brings little benefit today. But sometimes is not that easy to figure out which solution to a certain problem would perform better, specially when one is not very experienced. The author benchmarked different solutions to pick the best. This makes the library valuable. Some of the tools provided by the library are trivial to implement but very general purpose. So it's nice to have them in a library so that one doesn't need to reimplement one's own version repeatedly. Hence I find the library useful. 6. Did you try to use the library? With what compiler? Did you have any problems? Did not use it. 7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? Most of the time I spent studying the articles and the examples. I took a glance in the code. 8. Are you knowledgeable about the problem domain? Not much. I started doing some more heavy TMP only more recently. As I said, I don't know the other TMP libraries Roberto
On Sat, Jul 15, 2017 at 5:19 AM, Bjorn Reese via Boost < boost@lists.boost.org> wrote: [snip]
1. Should Mp11 be accepted into Boost? Please state all conditions for acceptance explicity.
MP11 should be ACCEPTED. I don't really have an opinion on the merits of
mp11 vs. Metal/Brigand/Kvasir, but I think MP11 has a home in Boost, and I'm glad it has been proposed. Apologies for the late review.
2. What is your evaluation of the design?
I really like the lightweight approach, and I especially appreciate the wide compatibility. The compatibility level is the biggest value to Boost here -- the effort required to achieve this is significant. The algorithm selection is fine. I'm not a fan of the mp_ prefix. I'm also not a fan of interfaces with "first", "second", and "third" (I prefer indices).
3. What is your evaluation of the implementation?
I think the manual unrolling of variadic templates is strange (reminds me of old C++), but I assume this is done for optimization (?). 4. What is your evaluation of the documentation?
It seems solid. I only skimmed it.
5. What is your evaluation of the potential usefulness of the library?
Useful, given its compatibility claims. I will continue to use Hana for my projects, but I would reach for this if I needed to support MSVC.
6. Did you try to use the library? With what compiler? Did you have any problems?
No.
7. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
I skimmed the unified header and the docs. I spent a total of about 90 minutes.
8. Are you knowledgeable about the problem domain?
Yes. I am very familiar with the implementation and usage of many metaprogramming libraries and techniques. Barrett Adair
participants (18)
-
Andrey Semashev
-
Barrett Adair
-
berne@notadragon.com
-
Bjorn Reese
-
Brook Milligan
-
Bruno Dutra
-
Edouard
-
Fletcher, John P
-
Joaquin M López Muñoz
-
Louis Dionne
-
Peter Dimov
-
Peter Koch Larsen
-
peterkochlarsen
-
Robert Ramey
-
Roberto Hinz
-
vijayan
-
Vinnie Falco
-
Zach Laine