diff options
Diffstat (limited to 'boost/hof/reveal.hpp')
-rw-r--r-- | boost/hof/reveal.hpp | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/boost/hof/reveal.hpp b/boost/hof/reveal.hpp new file mode 100644 index 0000000000..d8972b08cb --- /dev/null +++ b/boost/hof/reveal.hpp @@ -0,0 +1,385 @@ +/*============================================================================= + Copyright (c) 2014 Paul Fultz II + reveal.h + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#ifndef BOOST_HOF_GUARD_FUNCTION_REVEAL_H +#define BOOST_HOF_GUARD_FUNCTION_REVEAL_H + +/// 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: +/// +/// * [ConstInvocable](ConstInvocable) +/// * MoveConstructible +/// +/// 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); +/// } +/// + +#include <boost/hof/always.hpp> +#include <boost/hof/returns.hpp> +#include <boost/hof/is_invocable.hpp> +#include <boost/hof/identity.hpp> +#include <boost/hof/detail/move.hpp> +#include <boost/hof/detail/callable_base.hpp> +#include <boost/hof/detail/delegate.hpp> +#include <boost/hof/detail/holder.hpp> +#include <boost/hof/detail/join.hpp> +#include <boost/hof/detail/make.hpp> +#include <boost/hof/detail/static_const_var.hpp> +#include <boost/hof/detail/using.hpp> + +#ifndef BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS +#ifdef __clang__ +#define BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS 1 +#else +#define BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS 0 +#endif +#endif + +namespace boost { namespace hof { + +namespace detail { + + +template<class T, class=void> +struct has_failure +: std::false_type +{}; + +template<class T> +struct has_failure<T, typename holder< + typename T::failure +>::type> +: std::true_type +{}; + +struct identity_failure +{ + template<class T> + T operator()(T&& x); + + template<class T> + static T&& val(); +#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS + template<template<class...> class Template, class... Ts> + BOOST_HOF_USING(defer, Template<Ts...>); +#else + template<template<class...> class Template, class... Ts> + static auto defer(Ts&&...) -> Template<Ts...>; +#endif + +}; + +} + +template<class F, class=void> +struct get_failure +{ + template<class... Ts> + struct of + { +#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS + template<class Id> + using apply = decltype(Id()(std::declval<F>())(std::declval<Ts>()...)); +#else + template<class Id> + static auto apply(Id id) -> decltype(id(std::declval<F>())(std::declval<Ts>()...)); +#endif + }; +}; + +template<class F> +struct get_failure<F, typename std::enable_if<detail::has_failure<F>::value>::type> +: F::failure +{}; + +template<template<class...> class Template> +struct as_failure +{ + template<class... Ts> + struct of + { +#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS + template<class Id> + using apply = typename Id::template defer<Template, Ts...>; +#else + template<class Id> + static auto apply(Id) -> decltype(Id::template defer<Template, Ts...>()); +#endif + }; +}; + +namespace detail { +template<class Failure, class... Ts> +BOOST_HOF_USING_TYPENAME(apply_failure, Failure::template of<Ts...>); + +template<class F, class Failure> +struct reveal_failure +{ + // Add default constructor to make clang 3.4 happy + constexpr reveal_failure() + {} + // This is just a placeholder to produce a note in the compiler, it is + // never called + template< + class... Ts, + class=typename std::enable_if<(!is_invocable<F, Ts...>::value)>::type + > + constexpr auto operator()(Ts&&... xs) const +#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS + -> typename apply_failure<Failure, Ts...>::template apply<boost::hof::detail::identity_failure>; +#else + -> decltype(apply_failure<Failure, Ts...>::apply(boost::hof::detail::identity_failure())); +#endif +}; + +template<class F, class Failure=get_failure<F>, class=void> +struct traverse_failure +: reveal_failure<F, Failure> +{ + constexpr traverse_failure() + {} +}; + +template<class F, class Failure> +struct traverse_failure<F, Failure, typename holder< + typename Failure::children +>::type> +: Failure::children::template overloads<F> +{ + constexpr traverse_failure() + {} +}; + +template<class Failure, class Transform, class=void> +struct transform_failures +: Transform::template apply<Failure> +{}; + +template<class Failure, class Transform> +struct transform_failures<Failure, Transform, typename holder< + typename Failure::children +>::type> +: Failure::children::template transform<Transform> +{}; + +} + +template<class Failure, class... Failures> +struct failures; + +template<class... Fs> +struct with_failures +{ + typedef BOOST_HOF_JOIN(failures, Fs...) children; +}; + +template<class Failure, class... Failures> +struct failures +{ + template<class Transform> + BOOST_HOF_USING(transform, with_failures<detail::transform_failures<Failure, Transform>, detail::transform_failures<Failures, Transform>...>); + + template<class F, class FailureBase=BOOST_HOF_JOIN(failures, Failures...)> + struct overloads + : detail::traverse_failure<F, Failure>, FailureBase::template overloads<F> + { + constexpr overloads() + {} + using detail::traverse_failure<F, Failure>::operator(); + using FailureBase::template overloads<F>::operator(); + }; +}; + +template<class Failure> +struct failures<Failure> +{ + template<class Transform> + BOOST_HOF_USING(transform, with_failures<detail::transform_failures<Failure, Transform>>); + + template<class F> + BOOST_HOF_USING(overloads, detail::traverse_failure<F, Failure>); +}; + +template<class Transform, class... Fs> +struct failure_map +: with_failures<detail::transform_failures<get_failure<Fs>, Transform>...> +{}; + +template<class... Fs> +struct failure_for +: with_failures<get_failure<Fs>...> +{}; + +template<class F, class Base=detail::callable_base<F>> +struct reveal_adaptor +: detail::traverse_failure<Base>, Base +{ + typedef reveal_adaptor fit_rewritable1_tag; + using detail::traverse_failure<Base>::operator(); + using Base::operator(); + + BOOST_HOF_INHERIT_CONSTRUCTOR(reveal_adaptor, Base); +}; +// Avoid double reveals, it causes problem on gcc 4.6 +template<class F> +struct reveal_adaptor<reveal_adaptor<F>> +: reveal_adaptor<F> +{ + typedef reveal_adaptor fit_rewritable1_tag; + BOOST_HOF_INHERIT_CONSTRUCTOR(reveal_adaptor, reveal_adaptor<F>); +}; + +BOOST_HOF_DECLARE_STATIC_VAR(reveal, detail::make<reveal_adaptor>); + +}} // namespace boost::hof + +#endif |