• 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 
187 namespace boost { namespace hof {
188 
189 namespace detail {
190 
191 
192 template<class T, class=void>
193 struct has_failure
194 : std::false_type
195 {};
196 
197 template<class T>
198 struct has_failure<T, typename holder<
199     typename T::failure
200 >::type>
201 : std::true_type
202 {};
203 
204 struct 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 
223 template<class F, class=void>
224 struct 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 
239 template<class F>
240 struct get_failure<F, typename std::enable_if<detail::has_failure<F>::value>::type>
241 : F::failure
242 {};
243 
244 template<template<class...> class Template>
245 struct 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 
260 namespace detail {
261 template<class Failure, class... Ts>
262 BOOST_HOF_USING_TYPENAME(apply_failure, Failure::template of<Ts...>);
263 
264 template<class F, class Failure>
265 struct reveal_failure
266 {
267     // Add default constructor to make clang 3.4 happy
reveal_failureboost::hof::detail::reveal_failure268     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 
284 template<class F, class Failure=get_failure<F>, class=void>
285 struct traverse_failure
286 : reveal_failure<F, Failure>
287 {
traverse_failureboost::hof::detail::traverse_failure288     constexpr traverse_failure()
289     {}
290 };
291 
292 template<class F, class Failure>
293 struct traverse_failure<F, Failure, typename holder<
294     typename Failure::children
295 >::type>
296 : Failure::children::template overloads<F>
297 {
traverse_failureboost::hof::detail::traverse_failure298     constexpr traverse_failure()
299     {}
300 };
301 
302 template<class Failure, class Transform, class=void>
303 struct transform_failures
304 : Transform::template apply<Failure>
305 {};
306 
307 template<class Failure, class Transform>
308 struct transform_failures<Failure, Transform, typename holder<
309     typename Failure::children
310 >::type>
311 : Failure::children::template transform<Transform>
312 {};
313 
314 }
315 
316 template<class Failure, class... Failures>
317 struct failures;
318 
319 template<class... Fs>
320 struct with_failures
321 {
322     typedef BOOST_HOF_JOIN(failures, Fs...) children;
323 };
324 
325 template<class Failure, class... Failures>
326 struct 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     {
overloadsboost::hof::failures::overloads335         constexpr overloads()
336         {}
337         using detail::traverse_failure<F, Failure>::operator();
338         using FailureBase::template overloads<F>::operator();
339     };
340 };
341 
342 template<class Failure>
343 struct 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 
352 template<class Transform, class... Fs>
353 struct failure_map
354 : with_failures<detail::transform_failures<get_failure<Fs>, Transform>...>
355 {};
356 
357 template<class... Fs>
358 struct failure_for
359 : with_failures<get_failure<Fs>...>
360 {};
361 
362 template<class F, class Base=detail::callable_base<F>>
363 struct 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
373 template<class F>
374 struct 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 
381 BOOST_HOF_DECLARE_STATIC_VAR(reveal, detail::make<reveal_adaptor>);
382 
383 }} // namespace boost::hof
384 
385 #endif
386