Boost logo

Boost :

Subject: Re: [boost] "Simple C++11 metaprogramming"
From: Bruno Dutra (brunocodutra_at_[hidden])
Date: 2015-06-02 08:26:43


On Jun 2, 2015 7:43 AM, "Peter Dimov" <lists_at_[hidden]> wrote:
>
> Bruno Dutra wrote:
>
>> Still, laziness and SFINAE friendliness are the properties I deem most fundamental on any metaprogramming library.
>
>
> I meant to respond to these points too.
>
> The eagerness of template aliases is an obvious problem in at least one place and that place is mp_if, where the naive mp_if<P<T>, F<T>, G<T>> evaluates both F<T> and G<T>. But I'm not sure if there are others, under the assumption that we've no higher-order constructs. Eric Niebler's meta has an entire lazy:: namespace with deferred copies of all its constructs; continuing the general theme, I wonder whether all this is strictly needed once we jettison the lambda part.
>
> SFINAE friendliness can be quite a curse. It often transforms reasonably comprehensible error messages into full-scale Agatha Christie mysteries (the compiler output even being of comparable length). So I'm not convinced (at the moment) that a metaprogramming library should be SFINAE-friendly. I presently tend to lean towards the philosophy of static_assert'ing as much as possible, and leaving the primary templates undefined instead of empty.

That was *exactly* my approach in the beginning of my endeavor to
rewrite MPL, but that was only untill I had to write the first trait
introspection and realized static_assert'ions are not SFINAE friendly.

The need for laziness and SFINAE friendliness arises most clearly when
one is faced with a decision problem, whereby one is in the need for a
predicate such as is_evaluable<>, which expects a metafunction (a
variadic template template parameter in your case) and a variadic set
of arguments, and has to decided whether evaluating such metafunction
for the given set of arguments yields a valid result or errors out.
Errors could be due to arity mismatch or the incompleteness of the
instantiated template for example, but of course such predicates are
expected to dodge any such traps and yield either true_type or
false_type accordingly.

Perhaps one could go by without such predicates, but that would feel
just too crippling, it would be like programming C without branching.
Or maybe one should bite the bullet and rely on a lazy backend to
allow for such predicates (see below), but then why not exposing the
lazy backend to the end user if it's already there?

That's why I decided to follow a mixed approach, providing both lazy
and eager versions of metafunctions via the typename $<>::type and
$_t<> duality introduced by C++14.

//traits example using a lazy backend
#include <type_traits>

template<typename> struct test_c;
template<> struct test_c<void>{using type = void;};
template<typename _> using test = typename test_c<_>::type;

template<template<typename...> class f, typename... args>
struct is_evaluable_c
{
    template<template<typename...> class g, typename = g<args...>>
    static std::true_type check(int);
    template<template<typename...> class>
    static std::false_type check(...);

    using type = decltype(check<f>(0));
};

template<template<typename...> class f, typename... args>
using is_evaluable = typename is_evaluable_c<f, args...>::type;

static_assert(is_evaluable<test, void>::value, "");
static_assert(!is_evaluable<test, int>::value, "");
static_assert(!is_evaluable<test, void, int>::value, "");

Bruno Dutra


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