123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- /*=============================================================================
- 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
|