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