reveal

Description

The reveal function adaptor helps shows the error messages that get masked on some compilers. Sometimes an error in a function that causes a substitution failure, will remove the function from valid overloads. On compilers without a backtrace for substitution failure, this will mask the error inside the function. The reveal adaptor will expose these error messages while still keeping the function SFINAE-friendly.

Sample

If we take the print example from the quick start guide like this:

namespace adl {

using std::begin;

template<class R>
auto adl_begin(R&& r) BOOST_HOF_RETURNS(begin(r));
}

BOOST_HOF_STATIC_LAMBDA_FUNCTION(for_each_tuple) = [](const auto& sequence, auto f) BOOST_HOF_RETURNS
(
    boost::hof::unpack(boost::hof::proj(f))(sequence)
);

auto print = boost::hof::fix(boost::hof::first_of(
    [](auto, const auto& x) -> decltype(std::cout << x, void())
    {
        std::cout << x << std::endl;
    },
    [](auto self, const auto& range) -> decltype(self(*adl::adl_begin(range)), void())
    {
        for(const auto& x:range) self(x);
    },
    [](auto self, const auto& tuple) -> decltype(for_each_tuple(tuple, self), void())
    {
        return for_each_tuple(tuple, self);
    }
));

Which prints numbers and vectors:

print(5);

std::vector<int> v = { 1, 2, 3, 4 };
print(v);

However, if we pass a type that can’t be printed, we get an error like this:

print.cpp:49:5: error: no matching function for call to object of type 'boost::hof::fix_adaptor<boost::hof::first_of_adaptor<(lambda at print.cpp:29:9), (lambda at print.cpp:33:9), (lambda at print.cpp:37:9)> >'
    print(foo{});
    ^~~~~
fix.hpp:158:5: note: candidate template ignored: substitution failure [with Ts = <foo>]: no matching function for call to object of type 'const boost::hof::first_of_adaptor<(lambda at
      print.cpp:29:9), (lambda at print.cpp:33:9), (lambda at print.cpp:37:9)>'
    operator()(Ts&&... xs) const BOOST_HOF_SFINAE_RETURNS

Which is short and gives very little information why it can’t be called. It doesn’t even show the overloads that were try. However, using the reveal adaptor we can get more info about the error like this:

print.cpp:49:5: error: no matching function for call to object of type 'boost::hof::reveal_adaptor<boost::hof::fix_adaptor<boost::hof::first_of_adaptor<(lambda at print.cpp:29:9), (lambda at print.cpp:33:9),
      (lambda at print.cpp:37:9)> >, boost::hof::fix_adaptor<boost::hof::first_of_adaptor<(lambda at print.cpp:29:9), (lambda at print.cpp:33:9), (lambda at print.cpp:37:9)> > >'
    boost::hof::reveal(print)(foo{});
    ^~~~~~~~~~~~~~~~~~
reveal.hpp:149:20: note: candidate template ignored: substitution failure [with Ts = <foo>, $1 = void]: no matching function for call to object of type '(lambda at print.cpp:29:9)'
    constexpr auto operator()(Ts&&... xs) const
                   ^
reveal.hpp:149:20: note: candidate template ignored: substitution failure [with Ts = <foo>, $1 = void]: no matching function for call to object of type '(lambda at print.cpp:33:9)'
    constexpr auto operator()(Ts&&... xs) const
                   ^
reveal.hpp:149:20: note: candidate template ignored: substitution failure [with Ts = <foo>, $1 = void]: no matching function for call to object of type '(lambda at print.cpp:37:9)'
    constexpr auto operator()(Ts&&... xs) const
                   ^
fix.hpp:158:5: note: candidate template ignored: substitution failure [with Ts = <foo>]: no matching function for call to object of type 'const boost::hof::first_of_adaptor<(lambda at
      print.cpp:29:9), (lambda at print.cpp:33:9), (lambda at print.cpp:37:9)>'
    operator()(Ts&&... xs) const BOOST_HOF_SFINAE_RETURNS

So now the error has a note for each of the lambda overloads it tried. Of course this can be improved even further by providing custom reporting of failures.

Synopsis

template<class F>
reveal_adaptor<F> reveal(F f);

Requirements

F must be:

Reporting Failures

By default, reveal reports the substitution failure by trying to call the function. However, more detail expressions can be be reported from a template alias by using as_failure. This is done by defining a nested failure struct in the function object and then inheriting from as_failure. Also multiple failures can be reported by using with_failures.

Synopsis

// Report failure by instantiating the Template
template<template<class...> class Template>
struct as_failure;

// Report multiple falures
template<class... Failures>
struct with_failures;

// Report the failure for each function
template<class... Fs>
struct failure_for;

// Get the failure of a function
template<class F>
struct get_failure;

Example

#include <boost/hof.hpp>
#include <cassert>

struct sum_f
{
    template<class T, class U>
    using sum_failure = decltype(std::declval<T>()+std::declval<U>());

    struct failure
    : boost::hof::as_failure<sum_failure>
    {};

    template<class T, class U>
    auto operator()(T x, U y) const BOOST_HOF_RETURNS(x+y);
};

int main() {
    assert(sum_f()(1, 2) == 3);
}