[metal] Feature Complete - Request for Feedback
Dear community, As some of you might be aware through sporadic discussions on this mailing list throughout the last couple of years, I've been working for quite a while on a modern template metaprogramming library called Metal, that I advocate as a direct replacement for the aging Boost.MPL. I believe my work is finally complete now and so I come to you to request for feedback and ask whether you think that Metal would be a valuable addition to the Boost distribution. My intention is to request a formal review in the next days / weeks, depending on your preliminary feedback. In a glimpse, Metal is a pure template metaprogramming that explores all modern features available to C++14 to overcome the limitations of Boost.MPL, most notably: * vastly improved performance [1]; * terser syntax [2]. Repository: https://github.com/brunocodutra/metal Documentation: http://brunocodutra.github.io/metal/ Boost Incubator: http://blincubator.com/bi_library/metal-2/?gform_post_id=1566 I appreciate your feedback and opinion on whether Metal is a good fit for Boost! - Previous discussions About a year ago I came to this list to discuss a proposal of merging Metal into MPL as a new API built from ground up, while at the same time serving as a backend for the previous API on modern compilers. At that time, Metal was lazy and very similar to MPL, but since then it underwent great simplification and embraced the all-eager design, so I don't think this would be a good idea anymore. Besides, it soon became clear that this would be a huge undertaking with potential to inadvertently breaking a lot of good code to little gain, so I decided instead to tackle the problem migration from MPL by providing a handy adaptor [3]. [1]: http://metaben.ch/ [2]: http://brunocodutra.github.io/metal/index.html#in_a_glimpse [3]: http://brunocodutra.github.io/metal/group__external.html#gaede6847ad3e0bb64c... Regards, Bruno Dutra
From a first look at it, this looks quite awesome. What I do however wonder is, where does it fit in with MPL, Fusion and especially Hana? If you could give an overview of where your library is compared to those, you probably increase the support for your library. If there is a place besides the other libraries, I think it would fit in quite nicely. On a less important note: I'm not sure about the name, I immediately thought of bare-metal C++ when I heard that. But that might just be me. Am 20.02.2017 um 21:49 schrieb Bruno Dutra via Boost:
Dear community,
As some of you might be aware through sporadic discussions on this mailing list throughout the last couple of years, I've been working for quite a while on a modern template metaprogramming library called Metal, that I advocate as a direct replacement for the aging Boost.MPL.
I believe my work is finally complete now and so I come to you to request for feedback and ask whether you think that Metal would be a valuable addition to the Boost distribution. My intention is to request a formal review in the next days / weeks, depending on your preliminary feedback.
In a glimpse, Metal is a pure template metaprogramming that explores all modern features available to C++14 to overcome the limitations of Boost.MPL, most notably:
* vastly improved performance [1]; * terser syntax [2].
Repository: https://github.com/brunocodutra/metal Documentation: http://brunocodutra.github.io/metal/ Boost Incubator: http://blincubator.com/bi_library/metal-2/?gform_post_id=1566
I appreciate your feedback and opinion on whether Metal is a good fit for Boost!
On Mon, Feb 20, 2017 at 10:06 PM, Klemens Morgenstern via Boost < boost@lists.boost.org> wrote:
From a first look at it, this looks quite awesome.
Thanks!
What I do however wonder is, where does it fit in with MPL, Fusion and especially Hana? If you could give an overview of where your library is compared to those, you probably increase the support for your library.
That is a very valid concern and I wondered myself whether there was space for Metal in Boost for quite a while, before finally deciding to put this proposal forward. It basically boils down to whether template metaprogramming (TMP) is still useful to modern C++, given decltype and the paradigm shift it enables, which is very well explored by Hana. I believe that yes, TMP has its unique use cases, mainly because: * it scales much better in terms of both compile time and memory usage on all major compilers. To convince yourself, just check metaben.ch and be sure to play with the 'subtract baseline' switch; * It integrates nicely with standard type_traits and utilities; * It draws a clear line between type-level and value programming, which improves readability when the goal is to trigger SFINAE and drive overload resolution. Essentially I advocate you are better off using TMP if you are either dealing with a couple of dozens to thousands of elements or all you want is to constrain types to implement concept like requirements for functions templates. Now, since we still need TMP, the following question is whether MPL fits our needs. I posit that it can't possibly for several reasons, the most apparent being: * it is _very_ verbose, mainly because the necessary tools, such as alias templates, weren't available at the time it was developed; * it doesn't scale at all, because it relies on the preprocessor to emulate variadic templates, which is orders of magnitude slower and more memory hungry than pure TMP on a given compiler. Again you don't have to take my word for it, just browse metaben.ch. Finally, comparing Metal specifically to Hana, I believe it has some potential advantages besides what I've mentioned so far: * Metal is much lighter conceptually than Hana and in fact relies on only a couple of very simple concepts that are precisely defined early in the documentation, which are sufficient to formally describe every construct that the library provides [1] * being fined tuned for heterogeneous programming more than anything else, Hana happens to also support type level computations, while Metal is specifically designed for that end and is thus able to provide better tools in my opinion.
On a less important note: I'm not sure about the name, I immediately thought of bare-metal C++ when I heard that. But that might just be me.
To be honest neither was I when I picked it, however, after 2 years of development I learned to like it and started naming a couple of offspring projects after the same semantic domain, for example Alloy [2], where Metal is used as a backend to heterogeneous algorithm (it is very early in its development, but one can already see some use cases for Metal). [1]: http://brunocodutra.github.io/metal/#concepts [2]: https://github.com/brunocodutra/alloy Best regards, Bruno
Dear Bruno,
What I do however wonder is, where does it fit in with MPL, Fusion and especially Hana? If you could give an overview of where your library is compared to those, you probably increase the support for your library.
That is a very valid concern and I wondered myself whether there was space for Metal in Boost for quite a while, before finally deciding to put this proposal forward.
It basically boils down to whether template metaprogramming (TMP) is still useful to modern C++, given decltype and the paradigm shift it enables, which is very well explored by Hana. I believe that yes, TMP has its unique use cases, mainly because:
* it scales much better in terms of both compile time and memory usage on all major compilers. To convince yourself, just check metaben.ch and be sure to play with the 'subtract baseline' switch; * It integrates nicely with standard type_traits and utilities; * It draws a clear line between type-level and value programming, which improves readability when the goal is to trigger SFINAE and drive overload resolution.
I am on the skeptical side. I just learned boost::mpl and boost::fusion to implement compile-time computations for my proposed boost.histogram library, so I am by no means an expert on meta-programming. Nevertheless, I was positively surprised how fast I made progress with these libraries once I overcame my general attitude of "TMP is weiiiiird". The structural similarity of boost::mpl with the STL eased the learning curve a lot. At first glance (I am looking at the code in the section "In a Glimpse" of your documentation), I don't see much syntactic difference to boost::mpl, could you elaborate on the differences? It seems you mostly you drop the trailing ::type, because you use alias templates. I like your metal::lambda, I am not sure if boost::mpl has that. If not, one could probably add it to boost::mpl. I suppose my big question is this: can't you just merge your ideas/improvements into boost::mpl? With ifdefs you could enable C++14 features like templated aliases when they are available. Even more so, it should be possible to merge your speed improvements into boost::mpl. I also looked at your benchmarks and they are a bit confusing. I am mostly interested in how your lib compares to the mpl, but not all benchmarks have mpl data, and those that have (see at or count_if) only use up to 50 elements. Is that a hard limitation of the current mpl implementation? I am looking forward to the comments from the authors of boost::hana and boost::mpl. Best regards, Hans
On 21.02.2017 12:53, Hans Dembinski via Boost wrote:
I suppose my big question is this: can't you just merge your ideas/improvements into boost::mpl? With ifdefs you could enable C++14 features like templated aliases when they are available. Even more so, it should be possible to merge your speed improvements into boost::mpl.
The biggest and most useful feature of "new" TMP libraries is variadic templates that increase performance by a really huge factor. The problem with MPL is that vector type sequence must derive from vectorN-s, which are documented and can be used in template specialization. So it is impossible or pointless to remplement mpl::vector in terms of variadic parameters because of that. The other problem that I can quickly recall is recursive iterator-based approach, which also kills performance. Personally, I found it better from both performance and code sanity sides to extract types from mpl::vector to variadic sequence via partial template specialization (with mpl::vectorN<Ts...> using boost PP) and then pass variadic type sequence to whatever TMP library I'm using (brigand to be specific, but I think its unrelated in this case).
Best regards, Hans
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- -------- Mitsyn Sergey --- Это сообщение проверено на вирусы антивирусом Avast. https://www.avast.com/antivirus
Personally, I found it better from both performance and code sanity sides to extract types from mpl::vector to variadic sequence via partial template specialization (with mpl::vectorN<Ts...> using boost PP) and then pass variadic type sequence to whatever TMP library I'm using
You should be aware that this doesn't work in general, because MPL types are unspecified and pattern matching them actually means relying on implementation details. That is specially true for mpl::vector, which returns a proxy type that in turn masks the contents of the original vector whenever mpl::insert or mpl::erase is used, in a way that cannot be pattern matched like this. There is no portable way of transferring the contents of a MPL sequence to a variadic type, except for using MPL's own algorithms. If you check out the implementation of metal::from_mpl, you'll find that it relies on mpl::fold to extract the contents of sequences into a metal::list. This is by the way another advantage of Metal, since all of its types are precisely defined and friendly to pattern matching.
Am 21.02.2017 um 14:11 schrieb Bruno Dutra via Boost:
Personally, I found it better from both performance and code sanity sides to extract types from mpl::vector to variadic sequence via partial template specialization (with mpl::vectorN<Ts...> using boost PP) and then pass variadic type sequence to whatever TMP library I'm using
You should be aware that this doesn't work in general, because MPL types are unspecified and pattern matching them actually means relying on implementation details. That is specially true for mpl::vector, which returns a proxy type that in turn masks the contents of the original vector whenever mpl::insert or mpl::erase is used, in a way that cannot be pattern matched like this. There is no portable way of transferring the contents of a MPL sequence to a variadic type, except for using MPL's own algorithms. If you check out the implementation of metal::from_mpl, you'll find that it relies on mpl::fold to extract the contents of sequences into a metal::list.
This is by the way another advantage of Metal, since all of its types are precisely defined and friendly to pattern matching. I think that's your main selling point. Afaik Hana also relys on concepts more than on actual types; i.e. the result types are not completely specified. If that is the case and you provide those, you've got the place for your library.
Another dumb idea to make your life hell: do you think you could have some interface to hana? I.e. you get a sequence from hana and have a `decltype(metal::from_hana(seq))` to get a defined metal type? Or you have a `metal::to_hana` function.
Klemens Morgenstern wrote:
Am 21.02.2017 um 14:11 schrieb Bruno Dutra via Boost:
This is by the way another advantage of Metal, since all of its types are precisely defined and friendly to pattern matching.
I think that's your main selling point. Afaik Hana also relys on concepts more than on actual types; i.e. the result types are not completely specified.
This is a false dilemma; it's possible to take 'concepts' and return completely specified types. There are other legitimate reasons to prefer strictness in the arguments though - SFINAE friendliness is one. And there are reasons to prefer concept-ness. Such as for instance mp_transform<std::add_const_t, std::shared_ptr<X>> // std::shared_ptr<X const>
On Tue, Feb 21, 2017 at 4:32 PM, Peter Dimov via Boost < boost@lists.boost.org> wrote:
Klemens Morgenstern wrote:
Am 21.02.2017 um 14:11 schrieb Bruno Dutra via Boost:
This is by the way another advantage of Metal, since all of its types > are precisely defined and friendly to pattern matching.
I think that's your main selling point. Afaik Hana also relys on concepts more than on actual types; i.e. the result types are not completely specified.
This is a false dilemma; it's possible to take 'concepts' and return completely specified types.
There are other legitimate reasons to prefer strictness in the arguments though - SFINAE friendliness is one. And there are reasons to prefer concept-ness. Such as for instance
mp_transform<std::add_const_t, std::shared_ptr<X>> // std::shared_ptr<X const>
I'm glad you mentioned it, indeed there is no need for compromises there, we can have both, but I forgot to discuss how Metal addresses this. As a tool that makes it possible to drive overload resolution through SFINAE, it was a very important design goal for Metal from the very beginning that _everything_ it provides must be SFINAE friendly no matter what. In the beginning the concepts Metal was based on were more flexible and made it possible for example that any template specialization be used as a List. That alone however opens a whole trunk of worms, because one has to deal with stuff like this: join<std::tuple<>, std::map<X, Y>, std::unique_ptr<Z>> Should this be valid and if so what should be the result? One might think that std::tuple<X, Y, Z> would be the obvious answer, but how about default template parameters that both std::metal and std::unique_ptr have for their trailing parameters? Should it then be std::tuple<X, Y, std::less<X>, std::allocator<std::pair<X const, Y>>, Z, std::default_delete<Z>>? But why should the result List "type" be std::tuple? Shouldn't it instead by disallowed since the List "types" are not all the same? Conundrums like these made it very tricky to implement even the simplest of the algorithms and had a inevitable impact on performance. So I decided to simplify the concepts to the minimum necessary to express their underlying semantics, while providing helpers like metal::as_list and metal::as_number that translate from List-like and Number-like things into strict Metal Lists and Numbers. This proved to be the best design, because the implementation simplified dramatically, performance improved and metal::as_* helpers could be given maximum flexibility to convert the widest variety of *-like things into their equivalents. Your example is implemented in Metal like this using _ = metal::transform<metal::lambda<std::add_const_t>, metal::as_list<std::shared_ptr<X>>>; // metal::list<X const> metal::apply<metal::lambda<std::shared_ptr>, _> // std::shared_ptr<X const> Admittedly a bit verbosier, but not too bad. .
Bruno Dutra wrote:
That alone however opens a whole trunk of worms, because one has to deal with stuff like this:
join<std::tuple<>, std::map<X, Y>, std::unique_ptr<Z>>
Should this be valid and if so what should be the result?
Let me check...
Should it then be std::tuple<X, Y, std::less<X>, std::allocator<std::pair<X const, Y>>, Z, std::default_delete<Z>>?
Yes. :-)
But why should the result List "type" be std::tuple?
The type of the first list is used by convention.
Conundrums like these made it very tricky to implement even the simplest of the algorithms and had a inevitable impact on performance.
Well... it does make things a bit more convoluted here or there, but it's not that big of a burden.
On Tue, Feb 21, 2017 at 11:32 PM, Peter Dimov via Boost < boost@lists.boost.org> wrote:
Bruno Dutra wrote:
That alone however opens a whole trunk of worms, because one has to deal with stuff like this:
join<std::tuple<>, std::map<X, Y>, std::unique_ptr<Z>>
Should this be valid and if so what should be the result?
Let me check...
Should it then be std::tuple<X, Y, std::less<X>,
std::allocator<std::pair<X const, Y>>, Z, std::default_delete<Z>>?
Yes. :-)
But why should the result List "type" be std::tuple?
The type of the first list is used by convention.
Conundrums like these made it very tricky to implement even the simplest
of the algorithms and had a inevitable impact on performance.
Well... it does make things a bit more convoluted here or there, but it's not that big of a burden.
Good, so let us raise the bar a further notch ;) What about insert<std::map<X, Y>, number<2>, F>, should it yield std::map<X, Y, F>? Should it SFINAE away because std::map can't fit 5 elements? And how about erase<std::map<X, Y, F>, number<2>>, should it yield std::map<X, Y>? Should it SFINAE away because std::map can't fit less than 4 elements? Metal suffers of none of these issues, at the extra instantiation of metal::as_list, which BTW also serves as a clear indication of intent that helps other programmers understand the code and debug if necessary. Regards, Bruno
Bruno Dutra wrote:
Good, so let us raise the bar a further notch ;)
What about insert<std::map<X, Y>, number<2>, F>, should it yield std::map<X, Y, F>? Should it SFINAE away because std::map can't fit 5 elements?
Right, SFINAEability does make things harder. There's the option of taking everything and always returning metal::list. Numbers however pose fewer issues if I'm not mistaken. You just take N::value instead of requiring metal::number.
Bruno Dutra wrote:
mp_transform<std::add_const_t, std::shared_ptr<X>> // std::shared_ptr<X const>
Your example is implemented in Metal like this
using _ = metal::transform<metal::lambda<std::add_const_t>, metal::as_list<std::shared_ptr<X>>>; // metal::list<X const> metal::apply<metal::lambda<std::shared_ptr>, _> // std::shared_ptr<X const>
And suppose that std::shared_ptr<X> is given to you as a type P, and you don't know if it's shared_ptr or not, so you can't use metal::lambda<std::shared_ptr>?
On Wed, Feb 22, 2017 at 12:38 AM, Peter Dimov via Boost < boost@lists.boost.org> wrote:
Bruno Dutra wrote:
mp_transform<std::add_const_t, std::shared_ptr<X>> //
std::shared_ptr<X const>
Your example is implemented in Metal like this
using _ = metal::transform<metal::lambda<std::add_const_t>, metal::as_list<std::shared_ptr<X>>>; // metal::list<X const> metal::apply<metal::lambda<std::shared_ptr>, _> // std::shared_ptr<X
const>
And suppose that std::shared_ptr<X> is given to you as a type P, and you
don't know if it's shared_ptr or not, so you can't use metal::lambda<std::shared_ptr>? Excellent observation! We need metal::unwrap https://github.com/brunocodutra/metal/issues/58
Right, SFINAEability does make things harder. There's the option of taking everything and always returning metal::list. Numbers however pose fewer issues if I'm not mistaken. You just take N::value instead of requiring metal::number.
That is mostly true in the context of SFINAE friendliness, but that is not all that there's to it. Throughout Metal, the concept of equality among Values is expressed by metal::same, which is just a n-ary generalization of std::is_same. Because any type is a Value, it follows that all the information embedded in it must be available in its type signature as far as Metal is concerned. This is what guarantees pattern matching. This is also the reason why metal::number<N> is defined as an alias to std::integral_constant<metal::int_, N>, where metal::int_ is an unspecified integral type. This might seem unimportant, but was actually designed to solve an inconvenience of MPL, which depends on three different metafunctions to express the very same concept of equality, namely mpl::is_same, mpl::equal and mpl::equal_to. At any rate, metal::as_number conveniently transforms anything that has a nested ::value into a Number, provided that it fits metal::int_.
Bruno Dutra wrote:
And suppose that std::shared_ptr<X> is given to you as a type P, and you don't know if it's shared_ptr or not, so you can't use metal::lambda<std::shared_ptr>?
Excellent observation! We need metal::unwrap https://github.com/brunocodutra/metal/issues/58
FWIW, in mp11 for this purpose there's mp_assign, which is defined as mp_assign<L1<T1...>, L2<T2...>> -> L1<T2...> so that mp_assign<shared_ptr<X>, metal::list<X const>> is shared_ptr<X const>.
Throughout Metal, the concept of equality among Values is expressed by metal::same, which is just a n-ary generalization of std::is_same. [...]
This might seem unimportant, ...
It's not unimportant at all, I find these explanations useful as a design rationale. It certainly makes sense to keep everything in a normal form so that metal::same works across the board.
On Feb 21, 2017, at 1:16 PM, Bruno Dutra via Boost <boost@lists.boost.org> wrote:
Your example is implemented in Metal like this
using _ = metal::transform<metal::lambda<std::add_const_t>, metal::as_list<std::shared_ptr<X>>>; // metal::list<X const> metal::apply<metal::lambda<std::shared_ptr>, _> // std::shared_ptr<X const>
Admittedly a bit verbosier, but not too bad. .
I like this. Its explicit and fast, which I think is good for something that is core. From there you could build other frontends that simplify it more by deducing the explicit types and then passing them to the core components. Another thing I saw is that you no longer support gcc 4.8. Why is that? As a core library, I would expect good portability, since the portability limitations don’t just affect Metal but also any libraries that decide to use it(or perhaps replace mpl with it). Ideally, it would be nice if it supported gcc 4.6(even only partially), as I would use it in my Tick library and get rid of my homebrew metaprogamming. However, I know that might be a lot of work since gcc 4.6 doesn’t have template aliases. Paul
On Wed, Feb 22, 2017 at 2:33 AM, Paul Fultz II via Boost < boost@lists.boost.org> wrote:
On Feb 21, 2017, at 1:16 PM, Bruno Dutra via Boost < boost@lists.boost.org> wrote:
Your example is implemented in Metal like this
using _ = metal::transform<metal::lambda<std::add_const_t>, metal::as_list<std::shared_ptr<X>>>; // metal::list<X const> metal::apply<metal::lambda<std::shared_ptr>, _> // std::shared_ptr<X const>
Admittedly a bit verbosier, but not too bad. .
I like this. Its explicit and fast, which I think is good for something that is core. From there you could build other frontends that simplify it more by deducing the explicit types and then passing them to the core components.
Exactly. Thanks for your feedback!
Another thing I saw is that you no longer support gcc 4.8. Why is that? As a core library, I would expect good portability, since the portability limitations don’t just affect Metal but also any libraries that decide to use it(or perhaps replace mpl with it).
That is a very good point. I've strived to support the widest variety of compilers throughout the development of Metal and spent a _lot_ of time, working around nasty compiler bugs. It is kindda sad, but by now I'm sure I could ICE either GCC, Clang or MSVC at will. Simply put, at some point I just gave up on hammering Metal down on older GCC versions. I just tried to running Metal test suite on GCC 4.9 and it fails the 3rd test of 200 already. According to the error log, it can't SFINAE away invalid instantiations of a template that expects N arguments but was given less than that. It most probably can be worked around by decltyping a an overloaded function call, but that kills performance and I'll have to #ifdef it, which sucks TBH. Nevertheless I'll give it a shot https://github.com/brunocodutra/metal/issues/59 Ideally, it would be nice if it supported gcc 4.6(even only partially), as
I would use it in my Tick library and get rid of my homebrew metaprogamming. However, I know that might be a lot of work since gcc 4.6 doesn’t have template aliases.
Sadly without alias templates there's not much I could do :(
On Wed, 2017-02-22 at 19:35 +0100, Bruno Dutra wrote:
On Wed, Feb 22, 2017 at 2:33 AM, Paul Fultz II via Boost <boost@lists .boost.org> wrote:
On Feb 21, 2017, at 1:16 PM, Bruno Dutra via Boost <boost@lists.b oost.org> wrote:
Your example is implemented in Metal like this
using _ = metal::transform<metal::lambda<std::add_const_t>, metal::as_list<std::shared_ptr<X>>>; // metal::list<X const> metal::apply<metal::lambda<std::shared_ptr>, _> // std::shared_ptr<X const>
Admittedly a bit verbosier, but not too bad. .
I like this. Its explicit and fast, which I think is good for something that is core. From there you could build other frontends that simplify it more by deducing the explicit types and then passing them to the core components.
Another thing I saw is that you no longer support gcc 4.8. Why is that? As a core library, I would expect good portability, since the portability limitations don’t just affect Metal but also any libraries that decide to use it(or perhaps replace mpl with it). That is a very good point. I've strived to support the widest variety of compilers throughout the development of Metal and spent a _lot_ of time, working around nasty compiler bugs. It is kindda sad, but by now I'm sure I could ICE either GCC, Clang or MSVC at will. Simply
Exactly. Thanks for your feedback! put, at some point I just gave up on hammering Metal down on older GCC versions.
I just tried to running Metal test suite on GCC 4.9 and it fails the 3rd test of 200 already. According to the error log, it can't SFINAE away invalid instantiations of a template that expects N arguments but was given less than that.
It most probably can be worked around by decltyping a an overloaded function call, but that kills performance and I'll have to #ifdef it, which sucks TBH. Nevertheless I'll give it a shot https://github.com/brunocodutra/metal/issues/59
I think picking a slower implementation on older compilers is acceptable. This way I can develop with newer compilers which are fast, and deploy with the older compilers. Also, having a faster compilation with newer compilers is a good way to convince management to move to a newer compiler.
Ideally, it would be nice if it supported gcc 4.6(even only partially), as I would use it in my Tick library and get rid of my homebrew metaprogamming. However, I know that might be a lot of work since gcc 4.6 doesn’t have template aliases. Sadly without alias templates there's not much I could do :(
Well on gcc 4.6 inheritance can be used instead of an alias, but then you would need an evaluation step, like: METAL_EVAL(metal::drop<l3, metal::number<3>>) And then the library would need to apply these evaluations internally as well. I don't know know if you would accept a PR with changes to support gcc 4.6 or not. Of course the first thing is to support for gcc 4.8 and then we could move backwards toward 4.7 and then 4.6. Paul
On Feb 23, 2017 00:12, "paul via Boost"
Ideally, it would be nice if it supported gcc 4.6(even only partially), as I would use it in my Tick library and get rid of my homebrew metaprogamming. However, I know that might be a lot of work since gcc 4.6 doesn’t have template aliases. Sadly without alias templates there's not much I could do :(
Well on gcc 4.6 inheritance can be used instead of an alias, but then you would need an evaluation step, like: METAL_EVAL(metal::drop<l3, metal::number<3>>) And then the library would need to apply these evaluations internally as well. I don't know know if you would accept a PR with changes to support gcc 4.6 or not. Of course the first thing is to support for gcc 4.8 and then we could move backwards toward 4.7 and then 4.6. For a long time Metal was lazy just like you describe, so I can tell you from experience that it's not that simple. There are many reasons why, but 2 are most significant: 1. This might not be obvious at first, but while template<typename l, typename v> using append = join<l, list<v>>; is perfectly SFINAE friendly template<typename l, typename v> struct append : join<l, list<v>> {}; is not. 2. Alias templates don't introduce new entities that has to be instantiated, that is why one can't pattern match them. That means they are very lightweight for the compiler and the main reason why Metal can be so fast. This is most obvious to see in the implementations of metal::reverse, metal::rotate, metal::accumulate, which employ a very clever trick by Odin Holmes, that exploits alias templates to implement recursive algorothms that would otherwise be extremely slow using inheritance. You can watch his talk here https://youtu.be/cuNMtdE699E To make Metal lazy again, you have to essentially revert this PR: https://github.com/brunocodutra/metal/pull/32
Bruno Dutra On Tue, Feb 21, 2017 at 3:45 PM, Klemens Morgenstern via Boost < boost@lists.boost.org> wrote:
This is by the way another advantage of Metal, since all of its types are
precisely defined and friendly to pattern matching.
I think that's your main selling point. Afaik Hana also relys on concepts more than on actual types; i.e. the result types are not completely specified. If that is the case and you provide those, you've got the place for your library.
Absolutely! Every concept in Metal is defined solely by its type, no assumptions whatsoever are made about the presence of nested types or values [1]. As far as Metal is concerned, types need only be declared and may even be left undefined. [1]: http://brunocodutra.github.io/metal/#concepts Another dumb idea to make your life hell: do you think you could have some
interface to hana? I.e. you get a sequence from hana and have a `decltype(metal::from_hana(seq))` to get a defined metal type? Or you have a `metal::to_hana` function.
My intention is to expand the `external` module to provide adaptors to other libraries, Hana included, just like it provides for MPL now.
On Tue, Feb 21, 2017 at 10:53 AM, Hans Dembinski <hans.dembinski@gmail.com> wrote:
I am on the skeptical side. I just learned boost::mpl and boost::fusion
to implement compile-time computations for my proposed boost.histogram library, so I am by no means an expert on meta-programming. Nevertheless, I was positively surprised how fast I made progress with these libraries once I overcame my general attitude of "TMP is weiiiiird". The structural similarity of boost::mpl with the STL eased the learning curve a lot. Great! So you'll be mind-blown by what Metal can do with the help of modern C++ features!
At first glance (I am looking at the code in the section "In a Glimpse" of your documentation), I don't see much syntactic difference to boost::mpl, could you elaborate on the differences? It seems you mostly you drop the trailing ::type, because you use alias templates.
Metal is purposefully designed to resemble MPL stripped of all the `typename`s and `::type`s, but their similarities end there. MPL was written some 15 years ago, at a time that compilers that actually handled templates correctly were scarce. If you peek at MPL's source code you will promptly realize that half of it is really working around broken compilers, the other half being emulating missing language features that were introduced only much later in C++11. That is actually the greatest feat of MPL, heck it was written even before C++03 was standardized! Fortunately we can kiss all that heroic hackery goodbye, now that we have access to variadic templates and template alias, along with the fact that compilers of this decade are not all that bad anymore. This gives Metal a lot of flexibility to address other issues that MPL couldn't tackle back then, such as scalability and ease of use.
I like your metal::lambda, I am not sure if boost::mpl has that. If not, one could probably add it to boost::mpl.
MPL has something functionally similar called quote, except that mpl::quote doesn't actually exist, because it couldn't possibly be implemented without variadic templates. What do exist are its numbered relatives, mpl::quote1, mpl::quote2, ..., up to mpl::quote15, each able to handle only metafunctions that take exactly that many arguments, with no support for variadic metafunctions whatsoever. This a great example of MPL's greatest shortcoming: there was no other option back then, but to enumerate every possible specialization of templates, which entails a _lot_ of code repetition and the most gruesome preprocessor hackery you'll ever see.
I suppose my big question is this: can't you just merge your ideas/improvements into boost::mpl? With ifdefs you could enable C++14 features like templated aliases when they are available. Even more so, it should be possible to merge your speed improvements into boost::mpl.
It does sound like a good idea from the user's point of view since Metal looks so similar to MPL, but because they are so drastically distinct internally, merging them into a single library doesn't really make any sense. Fortunately, if you are coming from some legacy metaprogram, it is easy to incrementally migrate to Metal using the helper metal::from_mpl, but there is really no reason to ever use MPL anymore on a new project.
I also looked at your benchmarks and they are a bit confusing. I am mostly interested in how your lib compares to the mpl, but not all benchmarks have mpl data, and those that have (see at or count_if) only use up to 50 elements. Is that a hard limitation of the current mpl implementation?
Unfortunately that is indeed a hard limit of of Boost.MPL. Just like mpl::quote, everything else in MPL had to be implemented by enumerating every possible combination of arguments, which implies that some hard limit must have been chosen. That limit is 50 to sequence sizes and 15 to mpl::quote, mpl::apply and their friends. Metal doesn't have any theoretical limit and the number of arguments can easily reach the thousands in practice. Now I know that these arguments may sound a bit too abstract to someone not very experienced with template metaprogramming, but the advantages of modern C++ quickly become apparent when you try it out, so I invite you to give Metal a spin, so you can convince yourself.
I am looking forward to the comments from the authors of boost::hana and boost::mpl.
While the author of Hana is very active in the Boost community, it is unfortunately very unlikely that you'll hear from MPL authors in this mailing list nowadays. Regards, Bruno
Am 21.02.2017 um 14:03 schrieb Bruno Dutra via Boost:
On Tue, Feb 21, 2017 at 10:53 AM, Hans Dembinski <hans.dembinski@gmail.com> wrote:
[...]
I suppose my big question is this: can't you just merge your ideas/improvements into boost::mpl? With ifdefs you could enable C++14 features like templated aliases when they are available. Even more so, it should be possible to merge your speed improvements into boost::mpl.
It does sound like a good idea from the user's point of view since Metal looks so similar to MPL, but because they are so drastically distinct internally, merging them into a single library doesn't really make any sense. Fortunately, if you are coming from some legacy metaprogram, it is easy to incrementally migrate to Metal using the helper metal::from_mpl, but there is really no reason to ever use MPL anymore on a new project.
Could you elaborate a little more on this metal::from_mpl? How is it used? Can I just replace all occurrences of "boost::mpl" in any code by "metal::from_mpl" and it works?
I also looked at your benchmarks and they are a bit confusing. I am mostly interested in how your lib compares to the mpl, but not all benchmarks have mpl data, and those that have (see at or count_if) only use up to 50 elements. Is that a hard limitation of the current mpl implementation?
Unfortunately that is indeed a hard limit of of Boost.MPL. Just like mpl::quote, everything else in MPL had to be implemented by enumerating every possible combination of arguments, which implies that some hard limit must have been chosen. That limit is 50 to sequence sizes and 15 to mpl::quote, mpl::apply and their friends.
Just a short note: You can increase this upper limit by changing the values of some Boost.MPL macros and pre-generating headers for Boost.MPL-containers with these new values. For this, some python-scripts exist in "libs/mpl/preprocessed". However, they stopped working after the migration to Git from Subversion. And they were pretty complicated to use (and understand). Therefore, a few years ago, I created some helper python-script "boost_mpl_preprocess.py" which fixes pre-generating the headers and greatly simplifies the process. It is also located in the same directory in Boost-source since Boost 1.59. Just call it like this to see the options and its usage: python boost_mpl_preprocess.py --help At our company we had to increase the number of elements to 100 and it works fine except for the obvious reasons Bruno already talked about. It is so damn slow and wastes so much RAM that we can only compile single-threaded which slows down compile-performance even more. That is why I am so interested in a drop-in replacement for Boost.MPL and asked about metal::from_mpl above.
[...]
Regards, Bruno
Thanks, Deniz
On Tue, Feb 21, 2017 at 2:38 PM, Deniz Bahadir via Boost < boost@lists.boost.org> wrote:
Am 21.02.2017 um 14:03 schrieb Bruno Dutra via Boost:
On Tue, Feb 21, 2017 at 10:53 AM, Hans Dembinski < hans.dembinski@gmail.com> wrote:
[...]
I suppose my big question is this: can't you just merge your
ideas/improvements into boost::mpl? With ifdefs you could enable C++14 features like templated aliases when they are available. Even more so, it should be possible to merge your speed improvements into boost::mpl.
It does sound like a good idea from the user's point of view since Metal looks so similar to MPL, but because they are so drastically distinct internally, merging them into a single library doesn't really make any sense. Fortunately, if you are coming from some legacy metaprogram, it is easy to incrementally migrate to Metal using the helper metal::from_mpl, but there is really no reason to ever use MPL anymore on a new project.
Could you elaborate a little more on this metal::from_mpl? How is it used? Can I just replace all occurrences of "boost::mpl" in any code by "metal::from_mpl" and it works?
The idea is to be able to connect some preexisting MPL code to Metal, so that new features may be implemented using Metal already, before eventually porting everything in a gradual fashion and only if necessary. Thus metal::from_mpl converts MPL Sequences, Integral Constants and Metafunction Classes to their equivalents in Metal. It is recursive, so if you have an mpl::list of mpl::int_, it will produce a metal::list of metal::number. Check out the examples here: http://brunocodutra.github.io/metal/group__ external.html (click the panel to expand).
At our company we had to increase the number of elements to 100 and it works fine except for the obvious reasons Bruno already talked about. It is so damn slow and wastes so much RAM that we can only compile single-threaded which slows down compile-performance even more.
That is why I am so interested in a drop-in replacement for Boost.MPL and asked about metal::from_mpl above.
While Metal is not able to replace MPL like this, it can complement it with the help of metal::from_mpl, which makes it possible to rewrite critical sections that take the longest to compile for example. Give it a try, gains with respect to performance and memory consumption are huge! Regards, Bruno
Am 21.02.2017 um 18:07 schrieb Bruno Dutra via Boost:
On Tue, Feb 21, 2017 at 2:38 PM, Deniz Bahadir via Boost < boost@lists.boost.org> wrote:
Am 21.02.2017 um 14:03 schrieb Bruno Dutra via Boost:
On Tue, Feb 21, 2017 at 10:53 AM, Hans Dembinski < hans.dembinski@gmail.com> wrote:
[...]
I suppose my big question is this: can't you just merge your
ideas/improvements into boost::mpl? With ifdefs you could enable C++14 features like templated aliases when they are available. Even more so, it should be possible to merge your speed improvements into boost::mpl.
It does sound like a good idea from the user's point of view since Metal looks so similar to MPL, but because they are so drastically distinct internally, merging them into a single library doesn't really make any sense. Fortunately, if you are coming from some legacy metaprogram, it is easy to incrementally migrate to Metal using the helper metal::from_mpl, but there is really no reason to ever use MPL anymore on a new project.
Could you elaborate a little more on this metal::from_mpl? How is it used? Can I just replace all occurrences of "boost::mpl" in any code by "metal::from_mpl" and it works?
The idea is to be able to connect some preexisting MPL code to Metal, so that new features may be implemented using Metal already, before eventually porting everything in a gradual fashion and only if necessary. Thus metal::from_mpl converts MPL Sequences, Integral Constants and Metafunction Classes to their equivalents in Metal. It is recursive, so if you have an mpl::list of mpl::int_, it will produce a metal::list of metal::number. Check out the examples here: http://brunocodutra.github.io/metal/group__ external.html (click the panel to expand).
Ahh, clicking the panel makes sense. :-) I did not try this before and thought that doc-generation just failed there (or that none was available). Maybe, you should change the default view to be expanded (if possible).
At our company we had to increase the number of elements to 100 and it works fine except for the obvious reasons Bruno already talked about. It is so damn slow and wastes so much RAM that we can only compile single-threaded which slows down compile-performance even more.
That is why I am so interested in a drop-in replacement for Boost.MPL and asked about metal::from_mpl above.
While Metal is not able to replace MPL like this, it can complement it with the help of metal::from_mpl, which makes it possible to rewrite critical sections that take the longest to compile for example. Give it a try, gains with respect to performance and memory consumption are huge!
Our problem is, that we never really used Boost.MPL directly. We just got it for "free" as a (costly) dependency through other Boost libraries (e.g. Boost.MSM). So, I am not really looking forward to changing anything there. Using "find/replace", however, to just change the namespaces would have been a great (= easy) solution. But if find time I will see if Metal can help here.
Regards, Bruno
Thanks, Deniz
On Tue, Feb 21, 2017 at 7:21 PM, Deniz Bahadir via Boost < boost@lists.boost.org> wrote:
Ahh, clicking the panel makes sense. :-) I did not try this before and thought that doc-generation just failed there (or that none was available). Maybe, you should change the default view to be expanded (if possible).
Thanks for your feedback, it is now obvious that collapsible panels are not intuitive and need to be improved. I started a discussion about this here: https://github.com/brunocodutra/metal/issues/56
Our problem is, that we never really used Boost.MPL directly. We just got it for "free" as a (costly) dependency through other Boost libraries (e.g. Boost.MSM). So, I am not really looking forward to changing anything there. Using "find/replace", however, to just change the namespaces would have been a great (= easy) solution.
I believe this is a fairly common issue, because MPL has been around for so many years and so many core libraries depend on it. This has always been a personal concern of mine, in fact I even opened a side project within Metal that aims to address the problem by providing a minimal implementation of MPL that could be used as a drop in replacement for use cases like yours. See https://github.com/brunocodutra/metal/projects
On 2/21/17 10:21 AM, Deniz Bahadir via Boost wrote:
Am 21.02.2017 um 18:07 schrieb Bruno Dutra via Boost:
Our problem is, that we never really used Boost.MPL directly. We just got it for "free" as a (costly) dependency through other Boost libraries (e.g. Boost.MSM).
What's "costly" about it? What are you refering to? Robert Ramey
Thanks, Deniz
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Hi Robert Am 21.02.2017 um 21:41 schrieb Robert Ramey via Boost:
On 2/21/17 10:21 AM, Deniz Bahadir via Boost wrote:
Am 21.02.2017 um 18:07 schrieb Bruno Dutra via Boost:
Our problem is, that we never really used Boost.MPL directly. We just got it for "free" as a (costly) dependency through other Boost libraries (e.g. Boost.MSM).
What's "costly" about it? What are you refering to?
With "costly" I meant the compilation-times and the RAM-usage. (Especially on GCC.) But to be fair, probably it is not only Boost.MPLs fault. Some unlucky decisions on our side concerning the design intensified this.
Robert Ramey
Deniz
El feb. 20, 2017, a las 13:59, Bruno Dutra via Boost <boost@lists.boost.org> escribió:
On Mon, Feb 20, 2017 at 10:06 PM, Klemens Morgenstern via Boost < boost@lists.boost.org> wrote:
From a first look at it, this looks quite awesome.
Thanks!
What I do however wonder is, where does it fit in with MPL, Fusion and especially Hana? If you could give an overview of where your library is compared to those, you probably increase the support for your library.
That is a very valid concern and I wondered myself whether there was space for Metal in Boost for quite a while, before finally deciding to put this proposal forward.
It basically boils down to whether template metaprogramming (TMP) is still useful to modern C++, given decltype and the paradigm shift it enables, which is very well explored by Hana. I believe that yes, TMP has its unique use cases, mainly because:
* it scales much better in terms of both compile time and memory usage on all major compilers. To convince yourself, just check metaben.ch and be sure to play with the 'subtract baseline' switch; * It integrates nicely with standard type_traits and utilities; * It draws a clear line between type-level and value programming, which improves readability when the goal is to trigger SFINAE and drive overload resolution.
Essentially I advocate you are better off using TMP if you are either dealing with a couple of dozens to thousands of elements or all you want is to constrain types to implement concept like requirements for functions templates.
Now, since we still need TMP, the following question is whether MPL fits our needs. I posit that it can't possibly for several reasons, the most apparent being:
* it is _very_ verbose, mainly because the necessary tools, such as alias templates, weren't available at the time it was developed; * it doesn't scale at all, because it relies on the preprocessor to emulate variadic templates, which is orders of magnitude slower and more memory hungry than pure TMP on a given compiler. Again you don't have to take my word for it, just browse metaben.ch.
Finally, comparing Metal specifically to Hana, I believe it has some potential advantages besides what I've mentioned so far:
* Metal is much lighter conceptually than Hana and in fact relies on only a couple of very simple concepts that are precisely defined early in the documentation, which are sufficient to formally describe every construct that the library provides [1] * being fined tuned for heterogeneous programming more than anything else, Hana happens to also support type level computations, while Metal is specifically designed for that end and is thus able to provide better tools in my opinion.
I'm not really sure I fully understand your comparison to Hana. It sounds like you're saying that "Metal is smaller and simpler than Hana", in that Metal (1) has simpler/fewer concepts and (2) has a more restricted focus. It seems that Metal probably does something Hana does not, but I'm not sure what it is from this overview. When you propose Metal, I think it will be important to justify why Metal is useful in Boost even though Boost already has Hana. For example, is there something Metal does better (where "better" could be smaller code, faster code, faster compile, etc)? I think "simpler API" by itself is not quite enough, but I'm sure you have other reasons. It would be good to have a section of your proposal to show concrete examples.
On a less important note: I'm not sure about the name, I immediately thought of bare-metal C++ when I heard that. But that might just be me.
To be honest neither was I when I picked it, however, after 2 years of development I learned to like it and started naming a couple of offspring projects after the same semantic domain, for example Alloy [2], where Metal is used as a backend to heterogeneous algorithm (it is very early in its development, but one can already see some use cases for Metal).
[1]: http://brunocodutra.github.io/metal/#concepts [2]: https://github.com/brunocodutra/alloy
Best regards, Bruno
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
* Metal is much lighter conceptually than Hana and in fact relies on only a couple of very simple concepts that are precisely defined early in the documentation, which are sufficient to formally describe every construct that the library provides [1] * being fined tuned for heterogeneous programming more than anything else, Hana happens to also support type level computations, while Metal is specifically designed for that end and is thus able to provide better tools in my opinion.
I'm not really sure I fully understand your comparison to Hana.
It sounds like you're saying that "Metal is smaller and simpler than Hana", in that Metal (1) has simpler/fewer concepts and (2) has a more restricted focus. It seems that Metal probably does something Hana does not, but I'm not sure what it is from this overview.
My best understanding is that Hana is much more like Phoenix and just happens to also be able to do TMP. One of the things which annoys me about some of those who write "lightweight metaprogramming frameworks performance superior to Hana" with all sorts of wild unjustified claims is that they are usually comparing their TMP framework to Hana being used in a mode Hana was not intended by Louis to be its main use case. So of course a custom, specially written single purpose framework is going to beat a general purpose framework. So what. Bruno's Metal doesn't make extravagant claims based on unfair comparisons of apples to oranges, and is to be presented for peer review here. I therefore strongly support it over other lightweight template metaprogramming frameworks. I definitely think there is a space in Boost between full fat Hana being used in subset mode and a more specialised, single purpose TMP framework with significantly better time and space complexities. I've only skimmed the surface of Metal, but what I've seen so far looks impressive.
When you propose Metal, I think it will be important to justify why Metal is useful in Boost even though Boost already has Hana. For example, is there something Metal does better (where "better" could be smaller code, faster code, faster compile, etc)? I think "simpler API" by itself is not quite enough, but I'm sure you have other reasons. It would be good to have a section of your proposal to show concrete examples.
Hana's really, really fast at its primary intended use case. Louis put a ton of effort before starting Hana into benchmarking implementation techniques, and it shows. Hana being used for TMP isn't performance competitive, though it's way better than MPL or any of the previous gen libraries. Metal isn't the fastest of the new generation libraries from what I've been told, but it's competitive to them. That's my best understanding of the situation anyway, corrections are welcome. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Wed, Feb 22, 2017 at 12:20 PM, Niall Douglas via Boost < boost@lists.boost.org> wrote:
* Metal is much lighter conceptually than Hana and in fact relies on only a couple of very simple concepts that are precisely defined early in the documentation, which are sufficient to formally describe every construct that the library provides [1] * being fined tuned for heterogeneous programming more than anything else, Hana happens to also support type level computations, while Metal is specifically designed for that end and is thus able to provide better tools in my opinion.
I'm not really sure I fully understand your comparison to Hana.
It sounds like you're saying that "Metal is smaller and simpler than Hana", in that Metal (1) has simpler/fewer concepts and (2) has a more restricted focus. It seems that Metal probably does something Hana does not, but I'm not sure what it is from this overview.
My best understanding is that Hana is much more like Phoenix and just happens to also be able to do TMP. One of the things which annoys me about some of those who write "lightweight metaprogramming frameworks performance superior to Hana" with all sorts of wild unjustified claims is that they are usually comparing their TMP framework to Hana being used in a mode Hana was not intended by Louis to be its main use case. So of course a custom, specially written single purpose framework is going to beat a general purpose framework. So what.
Bruno's Metal doesn't make extravagant claims based on unfair comparisons of apples to oranges, and is to be presented for peer review here. I therefore strongly support it over other lightweight template metaprogramming frameworks. I definitely think there is a space in Boost between full fat Hana being used in subset mode and a more specialised, single purpose TMP framework with significantly better time and space complexities. I've only skimmed the surface of Metal, but what I've seen so far looks impressive.
Thanks a lot for your feedback Niall! We happen to share a very similar view of Hana. It's a fantastic tool for the processing of heterogeneous data sets that, because of its generality, also happens to do pure type metaprogramming, but that is far from it's core use case. Indeed, I just discussed on a previous email the fact that Hana can't be used effectively to control overload resolution of function calls, which is something to be expected from a pure TMP library, but not necessarily from a general heterogeneous library. In fact it is much trickier to provide strong SFINAE guarantees in the latter case and not at all a surprise that Louis decided not to provide them.
When you propose Metal, I think it will be important to justify why
Metal is useful in Boost even though Boost already has Hana. For example, is there something Metal does better (where "better" could be smaller code, faster code, faster compile, etc)? I think "simpler API" by itself is not quite enough, but I'm sure you have other reasons. It would be good to have a section of your proposal to show concrete examples.
Hana's really, really fast at its primary intended use case. Louis put a ton of effort before starting Hana into benchmarking implementation techniques, and it shows.
Hana being used for TMP isn't performance competitive, though it's way better than MPL or any of the previous gen libraries. Metal isn't the fastest of the new generation libraries from what I've been told, but it's competitive to them.
That's my best understanding of the situation anyway, corrections are welcome.
In the beginning, Metal used to be way slower than its contenders and somehow this unfortunate reputation stuck, but it is far from accurate nowadays. I'm unaware of other benchmarks, but as one can see on metaben.ch, Metal is actually the fastest on GCC, among Meta, Brigand and Hana, and only slightly slower than Brigand on Clang, its fastest contender. Still, even on Clang, Metal happens to be about an order of magnitude (!) faster than Brigand on some core algorithms that are probably most used, namely fold_left, fold_right, reverse and at.
On Wed, Feb 22, 2017 at 3:21 AM, Jared Grubb <jared.grubb@gmail.com> wrote:
I'm not really sure I fully understand your comparison to Hana.
It sounds like you're saying that "Metal is smaller and simpler than Hana", in that Metal (1) has simpler/fewer concepts and (2) has a more restricted focus.
Thanks for your feedback, I'll add a section to the documentation comparing Metal to Hana and MPL: https://github.com/brunocodutra/metal/issues/55
It seems that Metal probably does something Hana does not, but I'm not sure what it is from this overview.
Indeed there is something one can't do with Hana and that is exactly the very most important use case for Metal: control template overload resolution through SFINAE. Consider the following very simple toy example. ~~~ #include <boost/hana.hpp> namespace hana = boost::hana; template<typename X> constexpr auto fun(X const& x) -> decltype(void(hana::reverse(x)), true) { return true; } constexpr bool fun(...) { return false; } static_assert(fun(hana::tuple_t<>), ""); static_assert(!fun(0), ""); ~~~ fun() here doesn't mean anything or have any practical use, the only thing that matters in this example is that the static assertions should hold. Unfortunately however, because Hana does not provide any SFINAE guarantees, this example simply does not compile. As soon as fun(0) is instantiated, a hard error is triggered from within Hana in the first overload and there is nothing the user can do about it. That effectively means, one cannot control overload resolution using Hana. I should mention that this is not a flaw in Hana, it was just a design choice. Metal on the other hand is crafted exactly for that use case. It guarantees never to trigger a hard error, unless of course user provided metafunctions do that internally. This toy example looks like the following using Metal. ~~~ #include <metal.hpp> template<typename X> constexpr auto fun(X const& x) -> decltype(void(metal::reverse<X>{}), true) { return true; } constexpr bool fun(...) { return false; } static_assert(fun(metal::list<>{}), ""); static_assert(!fun(0), ""); ~~~ And compiles just fine.
[...]
Indeed there is something one can't do with Hana and that is exactly the very most important use case for Metal: control template overload resolution through SFINAE.
Consider the following very simple toy example.
~~~ #include <boost/hana.hpp> namespace hana = boost::hana;
template <typename X> constexpr auto fun(X const& x) -> decltype(void(hana::reverse(x)), true) { return true; }
constexpr bool fun(...) { return false; }
static_assert(fun(hana::tuple_t<>), ""); static_assert(!fun(0), ""); ~~~
[...]
To be fair, though, you can do this: ~~~ template<typename X, typename = std::enable_if_t<hana::Sequence<X>::value>> constexpr auto fun(X const& x) { return true; } ~~~ Here, `Sequence` is the concept required to have `hana::reverse`. In concept world, assuming a concept-ified Hana, you would write something like this instead: ~~~ template<hana::Sequence X> constexpr auto fun(X const& x) { return true; } constexpr auto fun(...) { return false; } ~~~ Also, I personally don't see the benefit of this SFINAE friendliness all the way down. In fact, I usually think that using a library that does that is more difficult, because when you mess up you don't get any information of why you messed up 20 instantiations down the stack. Instead, you just get a SFINAE failure at the top of the stack, with some back trace that may or may not be helpful to determine the cause of the initial failure. In any case, I do think that Metal (and Brigand) are useful libraries and Boost probably has room for one of those. I just don't think _this_ is the reason why. Louis -- View this message in context: http://boost.2283326.n4.nabble.com/metal-Feature-Complete-Request-for-Feedba... Sent from the Boost - Dev mailing list archive at Nabble.com.
On Sat, Feb 25, 2017 at 10:28 PM, Louis Dionne via Boost < boost@lists.boost.org> wrote:
[...]
Indeed there is something one can't do with Hana and that is exactly the very most important use case for Metal: control template overload resolution through SFINAE.
Consider the following very simple toy example.
~~~ #include <boost/hana.hpp> namespace hana = boost::hana;
template <typename X> constexpr auto fun(X const& x) -> decltype(void(hana::reverse(x)), true) { return true; }
constexpr bool fun(...) { return false; }
static_assert(fun(hana::tuple_t<>), ""); static_assert(!fun(0), ""); ~~~
[...]
To be fair, though, you can do this:
~~~ template<typename X, typename = std::enable_if_t<hana::Sequence<X>::value>> constexpr auto fun(X const& x) { return true; } ~~~
Here, `Sequence` is the concept required to have `hana::reverse`. In concept world, assuming a concept-ified Hana, you would write something like this instead:
~~~ template<hana::Sequence X> constexpr auto fun(X const& x) { return true; }
constexpr auto fun(...) { return false; } ~~~
Right, but that has the inconvenience of offloading explicit concept checking to the user, that better get it right or risk breaking overload resolution. BTW, you just gave us the perfect example of a use case for Metal: implementing such pseudo-concepts as hana::Sequence. Instead of trait-like, they could simply be made into a metafunction that triggers a substitution error with the help of Metal, such that one could get rid of std::enable_if and instead write the much more readable `typename = hana::Sequence<X>` directly. Also, by designing them such that they alias to std::integral_constant, they can also be used as traits if needed.
Also, I personally don't see the benefit of this SFINAE friendliness all the way down. In fact, I usually think that using a library that does that is more difficult, because when you mess up you don't get any information of why you messed up 20 instantiations down the stack. Instead, you just get a SFINAE failure at the top of the stack, with some back trace that may or may not be helpful to determine the cause of the initial failure.
That is very accurate in the context of heterogeneous contexpr algorithms, such as the ones implemented by Hana and one of the reasons why it is not surprising that Hana didn't take this path. In the context of template metaprogramming however it is the other way around. Errors triggered due to SFINAE unfriendly implementation details spill the obnoxious guts of template metaprogramming all over the error log and anyone that has played just a little bit with MPL can testify finding oneself scrolling through walls of incomprehensible nonsense trying in vain to pick up anything familiar that could hint to the underlying cause. SFINAE friendly interfaces however guarantee the error is triggered right there at the user facing API, placing the offending instantiation at the top of the error log, which points the user to the exact metafunction whose concept requirements have been violated, hiding away clever and very often not intuitive implementation details. Coupled with the concise concept system in Metal and an API that is precisely defined on top of them, the error can be promptly identified. If you are still skeptic about it, I invite you to try it out and see.
In any case, I do think that Metal (and Brigand) are useful libraries and Boost probably has room for one of those. I just don't think _this_ is the reason why.
I disagree, I've been playing with a library that implements heterogeneous algorithms on top of Metal as a way to find rough edges and polish them away before the API is frozen and I find the fact Metal is SFINAE friendly exceedingly useful to control overload resolution. I also found that it can be very useful to help reducing compile times, even if overload resolution can be unambiguously decided, by triggering substitution errors in the type signature of functions that are known not to be preferred, thus excluding potential viable overloads from the resolution step, which is way more onerous to the compiler. Bruno
First of all, I'd like to thank you for taking the time to take a look in Metal and give your feedback. Based on the various interesting discussions we had on this thread, I've implemented several changes to Metal, most of them to the documentation, so I'd like to invite you to take a second look at it, specially on the newly added F.A.Q [1] and on the greatly reworked sections about SFINAE-friendliness [2] and about migrating from Boost.MPL [3]. I appreciate any feedback, specially any questions that might remain unanswered. Bruno [1]: http://brunocodutra.github.io/metal/#FAQ [2]: http://brunocodutra.github.io/metal/#SFINAE [3]: http://brunocodutra.github.io/metal/#MPL
On Mon, Feb 20, 2017 at 3:49 PM, Bruno Dutra via Boost <boost@lists.boost.org> wrote:
Dear community,
As some of you might be aware through sporadic discussions on this mailing list throughout the last couple of years, I've been working for quite a while on a modern template metaprogramming library called Metal, that I advocate as a direct replacement for the aging Boost.MPL.
Regards, Bruno Dutra
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Will Metal be old as soon as Concepts appear? (ie probably C++20) Or are they complimentary? Tony
Will Metal be old as soon as Concepts appear? (ie probably C++20) Or are they complimentary?
Not at all, concepts are a replacement for std::enable_if and explicit SFINAE, but one still has to write the predicates themselves, which are not always as trivial as instantiating a type_trait. Specially when dealing with std::tuple and std::variant one may find oneself dealing with dozens to maybe hundreds of types, which in turn might have to be constraint in curious ways in order to express particular concepts. Metal will be there to make it easy.
participants (13)
-
Bruno Dutra
-
Deniz Bahadir
-
Gottlob Frege
-
Hans Dembinski
-
Jared Grubb
-
Klemens Morgenstern
-
Louis Dionne
-
Niall Douglas
-
paul
-
Paul Fultz II
-
Peter Dimov
-
Robert Ramey
-
Sergey Mitsyn