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