• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*=============================================================================
2    Copyright (c) 2014 Paul Fultz II
3    reveal.h
4    Distributed under the Boost Software License, Version 1.0. (See accompanying
5    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6==============================================================================*/
7
8#ifndef BOOST_HOF_GUARD_FUNCTION_REVEAL_H
9#define BOOST_HOF_GUARD_FUNCTION_REVEAL_H
10
11/// reveal
12/// ======
13///
14/// Description
15/// -----------
16///
17/// The `reveal` function adaptor helps shows the error messages that get
18/// masked on some compilers. Sometimes an error in a function that causes a
19/// substitution failure, will remove the function from valid overloads. On
20/// compilers without a backtrace for substitution failure, this will mask the
21/// error inside the function. The `reveal` adaptor will expose these error
22/// messages while still keeping the function SFINAE-friendly.
23///
24/// Sample
25/// ------
26///
27/// If we take the `print` example from the quick start guide like this:
28///
29///     namespace adl {
30///
31///     using std::begin;
32///
33///     template<class R>
34///     auto adl_begin(R&& r) BOOST_HOF_RETURNS(begin(r));
35///     }
36///
37///     BOOST_HOF_STATIC_LAMBDA_FUNCTION(for_each_tuple) = [](const auto& sequence, auto f) BOOST_HOF_RETURNS
38///     (
39///         boost::hof::unpack(boost::hof::proj(f))(sequence)
40///     );
41///
42///     auto print = boost::hof::fix(boost::hof::first_of(
43///         [](auto, const auto& x) -> decltype(std::cout << x, void())
44///         {
45///             std::cout << x << std::endl;
46///         },
47///         [](auto self, const auto& range) -> decltype(self(*adl::adl_begin(range)), void())
48///         {
49///             for(const auto& x:range) self(x);
50///         },
51///         [](auto self, const auto& tuple) -> decltype(for_each_tuple(tuple, self), void())
52///         {
53///             return for_each_tuple(tuple, self);
54///         }
55///     ));
56///
57/// Which prints numbers and vectors:
58///
59///     print(5);
60///
61///     std::vector<int> v = { 1, 2, 3, 4 };
62///     print(v);
63///
64/// However, if we pass a type that can't be printed, we get an error like
65/// this:
66///
67///     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)> >'
68///         print(foo{});
69///         ^~~~~
70///     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
71///           print.cpp:29:9), (lambda at print.cpp:33:9), (lambda at print.cpp:37:9)>'
72///         operator()(Ts&&... xs) const BOOST_HOF_SFINAE_RETURNS
73///
74/// Which is short and gives very little information why it can't be called.
75/// It doesn't even show the overloads that were try. However, using the
76/// `reveal` adaptor we can get more info about the error like this:
77///
78///     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),
79///           (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)> > >'
80///         boost::hof::reveal(print)(foo{});
81///         ^~~~~~~~~~~~~~~~~~
82///     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)'
83///         constexpr auto operator()(Ts&&... xs) const
84///                        ^
85///     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)'
86///         constexpr auto operator()(Ts&&... xs) const
87///                        ^
88///     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)'
89///         constexpr auto operator()(Ts&&... xs) const
90///                        ^
91///     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
92///           print.cpp:29:9), (lambda at print.cpp:33:9), (lambda at print.cpp:37:9)>'
93///         operator()(Ts&&... xs) const BOOST_HOF_SFINAE_RETURNS
94///
95/// So now the error has a note for each of the lambda overloads it tried. Of
96/// course this can be improved even further by providing custom reporting of
97/// failures.
98///
99/// Synopsis
100/// --------
101///
102///     template<class F>
103///     reveal_adaptor<F> reveal(F f);
104///
105/// Requirements
106/// ------------
107///
108/// F must be:
109///
110/// * [ConstInvocable](ConstInvocable)
111/// * MoveConstructible
112///
113/// Reporting Failures
114/// ------------------
115///
116/// By default, `reveal` reports the substitution failure by trying to call
117/// the function. However, more detail expressions can be be reported from a
118/// template alias by using `as_failure`. This is done by defining a nested
119/// `failure` struct in the function object and then inheriting from
120/// `as_failure`. Also multiple failures can be reported by using
121/// `with_failures`.
122///
123/// Synopsis
124/// --------
125///
126///     // Report failure by instantiating the Template
127///     template<template<class...> class Template>
128///     struct as_failure;
129///
130///     // Report multiple falures
131///     template<class... Failures>
132///     struct with_failures;
133///
134///     // Report the failure for each function
135///     template<class... Fs>
136///     struct failure_for;
137///
138///     // Get the failure of a function
139///     template<class F>
140///     struct get_failure;
141///
142/// Example
143/// -------
144///
145///     #include <boost/hof.hpp>
146///     #include <cassert>
147///
148///     struct sum_f
149///     {
150///         template<class T, class U>
151///         using sum_failure = decltype(std::declval<T>()+std::declval<U>());
152///
153///         struct failure
154///         : boost::hof::as_failure<sum_failure>
155///         {};
156///
157///         template<class T, class U>
158///         auto operator()(T x, U y) const BOOST_HOF_RETURNS(x+y);
159///     };
160///
161///     int main() {
162///         assert(sum_f()(1, 2) == 3);
163///     }
164///
165
166#include <boost/hof/always.hpp>
167#include <boost/hof/returns.hpp>
168#include <boost/hof/is_invocable.hpp>
169#include <boost/hof/identity.hpp>
170#include <boost/hof/detail/move.hpp>
171#include <boost/hof/detail/callable_base.hpp>
172#include <boost/hof/detail/delegate.hpp>
173#include <boost/hof/detail/holder.hpp>
174#include <boost/hof/detail/join.hpp>
175#include <boost/hof/detail/make.hpp>
176#include <boost/hof/detail/static_const_var.hpp>
177#include <boost/hof/detail/using.hpp>
178
179#ifndef BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS
180#ifdef __clang__
181#define BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS 1
182#else
183#define BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS 0
184#endif
185#endif
186
187namespace boost { namespace hof {
188
189namespace detail {
190
191
192template<class T, class=void>
193struct has_failure
194: std::false_type
195{};
196
197template<class T>
198struct has_failure<T, typename holder<
199    typename T::failure
200>::type>
201: std::true_type
202{};
203
204struct identity_failure
205{
206    template<class T>
207    T operator()(T&& x);
208
209    template<class T>
210    static T&& val();
211#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS
212    template<template<class...> class Template, class... Ts>
213    BOOST_HOF_USING(defer, Template<Ts...>);
214#else
215    template<template<class...> class Template, class... Ts>
216    static auto defer(Ts&&...) -> Template<Ts...>;
217#endif
218
219};
220
221}
222
223template<class F, class=void>
224struct get_failure
225{
226    template<class... Ts>
227    struct of
228    {
229#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS
230        template<class Id>
231        using apply = decltype(Id()(std::declval<F>())(std::declval<Ts>()...));
232#else
233        template<class Id>
234        static auto apply(Id id) -> decltype(id(std::declval<F>())(std::declval<Ts>()...));
235#endif
236    };
237};
238
239template<class F>
240struct get_failure<F, typename std::enable_if<detail::has_failure<F>::value>::type>
241: F::failure
242{};
243
244template<template<class...> class Template>
245struct as_failure
246{
247    template<class... Ts>
248    struct of
249    {
250#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS
251        template<class Id>
252        using apply = typename Id::template defer<Template, Ts...>;
253#else
254        template<class Id>
255        static auto apply(Id) -> decltype(Id::template defer<Template, Ts...>());
256#endif
257    };
258};
259
260namespace detail {
261template<class Failure, class... Ts>
262BOOST_HOF_USING_TYPENAME(apply_failure, Failure::template of<Ts...>);
263
264template<class F, class Failure>
265struct reveal_failure
266{
267    // Add default constructor to make clang 3.4 happy
268    constexpr reveal_failure()
269    {}
270    // This is just a placeholder to produce a note in the compiler, it is
271    // never called
272    template<
273        class... Ts,
274        class=typename std::enable_if<(!is_invocable<F, Ts...>::value)>::type
275    >
276    constexpr auto operator()(Ts&&... xs) const
277#if BOOST_HOF_REVEAL_USE_TEMPLATE_ALIAS
278        -> typename apply_failure<Failure, Ts...>::template apply<boost::hof::detail::identity_failure>;
279#else
280        -> decltype(apply_failure<Failure, Ts...>::apply(boost::hof::detail::identity_failure()));
281#endif
282};
283
284template<class F, class Failure=get_failure<F>, class=void>
285struct traverse_failure
286: reveal_failure<F, Failure>
287{
288    constexpr traverse_failure()
289    {}
290};
291
292template<class F, class Failure>
293struct traverse_failure<F, Failure, typename holder<
294    typename Failure::children
295>::type>
296: Failure::children::template overloads<F>
297{
298    constexpr traverse_failure()
299    {}
300};
301
302template<class Failure, class Transform, class=void>
303struct transform_failures
304: Transform::template apply<Failure>
305{};
306
307template<class Failure, class Transform>
308struct transform_failures<Failure, Transform, typename holder<
309    typename Failure::children
310>::type>
311: Failure::children::template transform<Transform>
312{};
313
314}
315
316template<class Failure, class... Failures>
317struct failures;
318
319template<class... Fs>
320struct with_failures
321{
322    typedef BOOST_HOF_JOIN(failures, Fs...) children;
323};
324
325template<class Failure, class... Failures>
326struct failures
327{
328    template<class Transform>
329    BOOST_HOF_USING(transform, with_failures<detail::transform_failures<Failure, Transform>, detail::transform_failures<Failures, Transform>...>);
330
331    template<class F, class FailureBase=BOOST_HOF_JOIN(failures, Failures...)>
332    struct overloads
333    : detail::traverse_failure<F, Failure>, FailureBase::template overloads<F>
334    {
335        constexpr overloads()
336        {}
337        using detail::traverse_failure<F, Failure>::operator();
338        using FailureBase::template overloads<F>::operator();
339    };
340};
341
342template<class Failure>
343struct failures<Failure>
344{
345    template<class Transform>
346    BOOST_HOF_USING(transform, with_failures<detail::transform_failures<Failure, Transform>>);
347
348    template<class F>
349    BOOST_HOF_USING(overloads, detail::traverse_failure<F, Failure>);
350};
351
352template<class Transform, class... Fs>
353struct failure_map
354: with_failures<detail::transform_failures<get_failure<Fs>, Transform>...>
355{};
356
357template<class... Fs>
358struct failure_for
359: with_failures<get_failure<Fs>...>
360{};
361
362template<class F, class Base=detail::callable_base<F>>
363struct reveal_adaptor
364: detail::traverse_failure<Base>, Base
365{
366    typedef reveal_adaptor fit_rewritable1_tag;
367    using detail::traverse_failure<Base>::operator();
368    using Base::operator();
369
370    BOOST_HOF_INHERIT_CONSTRUCTOR(reveal_adaptor, Base);
371};
372// Avoid double reveals, it causes problem on gcc 4.6
373template<class F>
374struct reveal_adaptor<reveal_adaptor<F>>
375: reveal_adaptor<F>
376{
377    typedef reveal_adaptor fit_rewritable1_tag;
378    BOOST_HOF_INHERIT_CONSTRUCTOR(reveal_adaptor, reveal_adaptor<F>);
379};
380
381BOOST_HOF_DECLARE_STATIC_VAR(reveal, detail::make<reveal_adaptor>);
382
383}} // namespace boost::hof
384
385#endif
386