reveal.hpp 12 KB

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