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
* 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?
> * 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.
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
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:
Everything is `constexpr` where possible, and the author has
implemented straightforward optimizations to reduce compilation times
for the heavier functions, such as these:
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
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
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.
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
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/91f2b32cf5a27b3aeaaf6ceb42959c677410f490
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
> 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!
> 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.
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
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.
> 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.
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
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
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
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
> 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.
> 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.
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.
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
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
> 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
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
> 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/85d6d32f6e58e648c4573189f8bf2d7633604f27/include/metal/external/mpl.hpp
[2]:
https://github.com/brunocodutra/metal/blob/85d6d32f6e58e648c4573189f8bf2d7633604f27/example/src/mpl.cpp#L10
Bruno
> On Jul 15, 2017, at 4:19 AM, Bjorn Reese via Boost <bo...@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
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
> 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?
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
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?
Quite high; should replace a lot of current boost MPL usage.
> 6. Did you try to use the library? With what compiler? Did you have any
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-a...@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
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
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