Boost logo

Boost :

Subject: Re: [boost] [metal] Feature Complete - Request for Feedback
From: Bruno Dutra (brunocodutra_at_[hidden])
Date: 2017-02-25 23:05:55


On Sat, Feb 25, 2017 at 10:28 PM, Louis Dionne via Boost <
boost_at_[hidden]> 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 &lt;boost/hana.hpp&gt;
> > 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&lt;hana::Sequence&lt;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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk