call_if.hpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. #ifndef BOOST_CONTRACT_CALL_IF_HPP_
  2. #define BOOST_CONTRACT_CALL_IF_HPP_
  3. // Copyright (C) 2008-2018 Lorenzo Caminiti
  4. // Distributed under the Boost Software License, Version 1.0 (see accompanying
  5. // file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).
  6. // See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html
  7. /** @file
  8. Statically disable compilation and execution of functor calls.
  9. @note This facility allows to emulate C++17 <c>if constexpr</c> statements
  10. when used together with functor templates (or C++14 generic lambdas).
  11. */
  12. #include <boost/contract/detail/none.hpp>
  13. #include <boost/make_shared.hpp>
  14. #include <boost/shared_ptr.hpp>
  15. #include <boost/utility/enable_if.hpp>
  16. #include <boost/config.hpp>
  17. /* PRIVATE */
  18. /** @cond */
  19. // Boost.ResultOf not always able to deduce lambda result type (on MSVC).
  20. #ifndef BOOST_NO_CXX11_DECLTYPE
  21. #include <boost/utility/declval.hpp>
  22. #define BOOST_CONTRACT_CALL_IF_RESULT_OF_(F) \
  23. decltype(boost::declval<F>()())
  24. #else
  25. #include <boost/utility/result_of.hpp>
  26. #define BOOST_CONTRACT_CALL_IF_RESULT_OF_(F) \
  27. typename boost::result_of<F()>::type
  28. #endif
  29. /** @endcond */
  30. /* CODE */
  31. namespace boost { namespace contract {
  32. /**
  33. Select compilation and execution of functor template calls using a static
  34. boolean predicate.
  35. This class template has no members because it is never used directly, it is only
  36. used via its specializations.
  37. Usually this class template is instantiated only via the return value of
  38. @RefFunc{boost::contract::call_if} and @RefFunc{boost::contract::call_if_c}.
  39. @see @RefSect{extras.assertion_requirements__templates_,
  40. Assertion Requirements}
  41. @tparam Pred Static boolean predicate that selects which functor template
  42. call to compile and execute.
  43. @tparam Then Type of the functor template to call if the static predicate
  44. @c Pred is @c true.
  45. @tparam ThenResult Return type of then-branch functor template call (this is
  46. usually automatically deduced by this library so it is never explicitly
  47. specified by the user, and that is why it is often marked as
  48. @c internal_type in this documentation).
  49. */
  50. template<bool Pred, typename Then, typename ThenResult =
  51. #ifndef BOOST_CONTRACT_DETAIL_DOXYGEN
  52. boost::contract::detail::none
  53. #else
  54. internal_type
  55. #endif
  56. >
  57. struct call_if_statement {}; // Empty so cannot be used (but copyable).
  58. /**
  59. Template specialization to dispatch between then-branch functor template calls
  60. that return void and the ones that return non-void.
  61. The base class is a call-if statement so the else and else-if statements can be
  62. specified if needed.
  63. Usually this class template is instantiated only via the return value of
  64. @RefFunc{boost::contract::call_if} and @RefFunc{boost::contract::call_if_c}.
  65. @note The <c>result_of<Then()>::type</c> expression should be evaluated only
  66. when the static predicate is already checked to be @c true (because
  67. @c Then() is required to compile only in that case).
  68. Thus, this template specialization introduces an extra level of
  69. indirection necessary for proper lazy evaluation of this result-of
  70. expression.
  71. @see @RefSect{extras.assertion_requirements__templates_,
  72. Assertion Requirements}
  73. @tparam Then Type of functor template to call when the static predicate is
  74. @c true (as it is for this template specialization).
  75. */
  76. template<typename Then>
  77. struct call_if_statement<true, Then,
  78. #ifndef BOOST_CONTRACT_DETAIL_DOXYGEN
  79. boost::contract::detail::none
  80. #else
  81. internal_type
  82. #endif
  83. > :
  84. call_if_statement<true, Then,
  85. #ifndef BOOST_CONTRACT_DETAIL_DOXYGEN
  86. BOOST_CONTRACT_CALL_IF_RESULT_OF_(Then)
  87. #else
  88. typename result_of<Then()>::type
  89. #endif
  90. >
  91. { // Copyable (as its base).
  92. /**
  93. Construct this object with the then-branch functor template.
  94. @param f Then-branch nullary functor template.
  95. The functor template call @c f() is compiled and called for this
  96. template specialization (because the if-statement static
  97. predicate is @c true).
  98. The return type of @c f() must be the same as (or implicitly
  99. convertible to) the return type of all other functor template
  100. calls specified for this call-if object.
  101. */
  102. explicit call_if_statement(Then f) : call_if_statement<true, Then,
  103. BOOST_CONTRACT_CALL_IF_RESULT_OF_(Then)>(f) {}
  104. };
  105. /**
  106. Template specialization to handle static predicates that are @c true for
  107. then-branch functor template calls that do not return void.
  108. Usually this class template is instantiated only via the return value of
  109. @RefFunc{boost::contract::call_if} and @RefFunc{boost::contract::call_if_c}.
  110. @see @RefSect{extras.assertion_requirements__templates_,
  111. Assertion Requirements}
  112. @tparam Then Type of functor template to call when the static predicate is
  113. @c true (as it is for this template specialization).
  114. @tparam ThenResult Non-void return type of the then-branch functor template
  115. call.
  116. */
  117. template<typename Then, typename ThenResult>
  118. struct call_if_statement<true, Then, ThenResult> { // Copyable (as *).
  119. /**
  120. Construct this object with the then-branch functor template.
  121. @param f Then-branch nullary functor template.
  122. The functor template call @c f() is actually compiled and
  123. executed for this template specialization (because the
  124. if-statement static predicate is @c true).
  125. The return type of @c f() must be the same as (or implicitly
  126. convertible to) the @p ThenResult type.
  127. */
  128. explicit call_if_statement(Then f) :
  129. r_(boost::make_shared<ThenResult>(f())) {}
  130. /**
  131. This implicit type conversion returns a copy of the value returned by the
  132. call to the then-branch functor template.
  133. */
  134. operator ThenResult() const { return *r_; }
  135. /**
  136. Specify the else-branch functor template.
  137. @param f Else-branch nullary functor template.
  138. The functor template call @c f() is never compiled and executed
  139. for this template specialization (because the if-statement
  140. static predicate is @c true).
  141. The return type of @c f() must be the same as (or implicitly
  142. convertible to) the @p ThenResult type.
  143. @return A copy of the value returned by the call to the then-branch functor
  144. template (because the else-branch functor template call is not
  145. executed).
  146. */
  147. template<typename Else>
  148. ThenResult else_(Else const& f) const { return *r_; }
  149. /**
  150. Specify an else-if-branch functor template (using a static boolean
  151. predicate).
  152. @param f Else-if-branch nullary functor template.
  153. The functor template call @c f() is never compiled and executed
  154. for this template specialization (because the if-statement
  155. static predicate is @c true).
  156. The return type of @c f() must be the same as (or implicitly
  157. convertible to) the @p ThenResult type.
  158. @tparam ElseIfPred Static boolean predicate selecting which functor
  159. template call to compile and execute.
  160. @return A call-if statement so the else statement and additional else-if
  161. statements can be specified if needed.
  162. Eventually, it will be the return value of the then-branch functor
  163. template call for this template specialization (because the
  164. if-statement static predicate is @c true).
  165. */
  166. template<bool ElseIfPred, typename ElseIfThen>
  167. call_if_statement<true, Then, ThenResult> else_if_c(ElseIfThen const& f)
  168. const { return *this; }
  169. /**
  170. Specify an else-if-branch functor template (using a nullary boolean
  171. meta-function).
  172. @param f Else-if-branch nullary functor template.
  173. The functor template call @c f() is never compiled and executed
  174. for this template specialization (because the if-statement
  175. static predicate is @c true).
  176. The return type of @c f() must be the same as (or implicitly
  177. convertible to) the @p ThenResult type.
  178. @tparam ElseIfPred Nullary boolean meta-function selecting which functor
  179. template call to compile and execute.
  180. @return A call-if statement so the else statement and additional else-if
  181. statements can be specified if needed.
  182. Eventually, it will be the return value of the then-branch functor
  183. template call for this template specialization (because the
  184. if-statement static predicate is @c true).
  185. */
  186. template<class ElseIfPred, typename ElseIfThen>
  187. call_if_statement<true, Then, ThenResult> else_if(ElseIfThen const& f)
  188. const { return *this; }
  189. private:
  190. boost::shared_ptr<ThenResult> r_;
  191. };
  192. /**
  193. Template specialization to handle static predicates that are @c true for
  194. then-branch functor template calls that return void.
  195. Usually this class template is instantiated only via the return value of
  196. @RefFunc{boost::contract::call_if} and @RefFunc{boost::contract::call_if_c}.
  197. @see @RefSect{extras.assertion_requirements__templates_,
  198. Assertion Requirements}
  199. @tparam Then Type of functor template to call when the static predicate if
  200. @c true (as it is for this template specialization).
  201. */
  202. template<typename Then>
  203. struct call_if_statement<true, Then, void> { // Copyable (no data).
  204. /**
  205. Construct this object with the then-branch functor template.
  206. @param f Then-branch nullary functor template.
  207. The functor template call @c f() is actually compiled and
  208. executed for this template specialization (because the
  209. if-statement static predicate is @c true).
  210. The return type of @c f() must be @c void for this template
  211. specialization (because the then-branch functor template calls
  212. return void).
  213. */
  214. explicit call_if_statement(Then f) { f(); }
  215. // Cannot provide `operator ThenResult()` here, because ThenResult is void.
  216. /**
  217. Specify the else-branch functor template.
  218. @param f Else-branch nullary functor template.
  219. The functor template call @c f() is never compiled and executed
  220. for this template specialization (because the if-statement
  221. static predicate is @c true).
  222. The return type of @c f() must be @c void for this template
  223. specialization (because the then-branch functor template calls
  224. return void).
  225. */
  226. template<typename Else>
  227. void else_(Else const& f) const {}
  228. /**
  229. Specify an else-if-branch functor template (using a static boolean
  230. predicate).
  231. @param f Else-if-branch nullary functor template.
  232. The functor template call @c f() is never compiled and executed
  233. for this template specialization (because the if-statement
  234. static predicate is @c true).
  235. The return type of @c f() must be @c void for this template
  236. specialization (because the then-branch functor template calls
  237. return void).
  238. @tparam ElseIfPred Static boolean predicate selecting which functor
  239. template call to compile and execute.
  240. @return A call-if statement so the else statement and additional else-if
  241. statements can be specified if needed.
  242. Eventually, it will return void for this template specialization
  243. (because the then-branch functor template calls return void).
  244. */
  245. template<bool ElseIfPred, typename ElseIfThen>
  246. call_if_statement<true, Then, void> else_if_c(ElseIfThen const& f) const {
  247. return *this;
  248. }
  249. /**
  250. Specify an else-if-branch functor template (using a nullary boolean
  251. meta-function).
  252. @param f Else-if-branch nullary functor template.
  253. The functor template call @c f() is never compiled and executed
  254. for this template specialization (because the if-statement
  255. static predicate is @c true).
  256. The return type of @c f() must be @c void for this template
  257. specialization (because the then-branch functor template calls
  258. return void).
  259. @tparam ElseIfPred Nullary boolean meta-function selecting which functor
  260. template call to compile and execute.
  261. @return A call-if statement so the else statement and additional else-if
  262. statements can be specified if needed.
  263. Eventually, it will return void for this template specialization
  264. (because the then-branch functor template calls return void).
  265. */
  266. template<class ElseIfPred, typename ElseIfThen>
  267. call_if_statement<true, Then, void> else_if(ElseIfThen const& f) const {
  268. return *this;
  269. }
  270. };
  271. /**
  272. Template specialization to handle static predicates that are @c false.
  273. This template specialization handles all else-branch functor template calls
  274. (whether they return void or not).
  275. Usually this class template is instantiated only via the return value of
  276. @RefFunc{boost::contract::call_if} and @RefFunc{boost::contract::call_if_c}.
  277. @see @RefSect{extras.assertion_requirements__templates_,
  278. Assertion Requirements}
  279. @tparam Then Type of functor template to call when the static predicate is
  280. @c true (never the case for this template specialization).
  281. */
  282. template<typename Then> // Copyable (no data).
  283. struct call_if_statement<false, Then,
  284. #ifndef BOOST_CONTRACT_DETAIL_DOXYGEN
  285. boost::contract::detail::none
  286. #else
  287. internal_type
  288. #endif
  289. > {
  290. /**
  291. Construct this object with the then-branch functor template.
  292. @param f Then-branch nullary functor template.
  293. The functor template call @c f() is never compiled and executed
  294. for this template specialization (because the if-statement
  295. static predicate is @c false).
  296. The return type of @c f() must be the same as (or implicitly
  297. convertible to) the return type of all other functor template
  298. calls specified for this call-if object.
  299. */
  300. explicit call_if_statement(Then const& f) {}
  301. // Do not provide `operator result_type()` here, require else_ instead.
  302. /**
  303. Specify the else-branch functor template.
  304. @note The <c>result_of<Else()>::type</c> expression should be evaluated
  305. only when the static predicate is already checked to be @c false
  306. (because @c Else() is required to compile only in that case).
  307. Thus, this result-of expression is evaluated lazily only in
  308. instantiations of this template specialization.
  309. @param f Else-branch nullary functor template.
  310. The functor template call @c f() is actually compiled and
  311. executed for this template specialization (because the
  312. if-statement static predicate is @c false).
  313. The return type of @c f() must be the same as (or implicitly
  314. convertible to) the return type of all other functor template
  315. calls specified for this call-if object.
  316. @return A copy of the value returned by the call to the else-branch functor
  317. template @c f().
  318. */
  319. template<typename Else>
  320. #ifndef BOOST_CONTRACT_DETAIL_DOXYGEN
  321. BOOST_CONTRACT_CALL_IF_RESULT_OF_(Else)
  322. #else
  323. typename result_of<Else()>::type
  324. #endif
  325. else_(Else f) const { return f(); }
  326. /**
  327. Specify an else-if-branch functor template (using a static boolean
  328. predicate).
  329. @param f Else-if-branch nullary functor template.
  330. The functor template call @c f() is actually compiled and
  331. executed if and only if @c ElseIfPred is @c true (because the
  332. if-statement static predicate is already @c false for this
  333. template specialization).
  334. The return type of @c f() must be the same as (or implicitly
  335. convertible to) the return type of all other functor template
  336. calls specified for this call-if object.
  337. @tparam ElseIfPred Static boolean predicate selecting which functor
  338. template call to compile and execute.
  339. @return A call-if statement so the else statement and additional else-if
  340. statements can be specified if needed.
  341. Eventually, this will be the return value of the functor template
  342. call being compiled and executed.
  343. */
  344. template<bool ElseIfPred, typename ElseIfThen>
  345. call_if_statement<ElseIfPred, ElseIfThen> else_if_c(ElseIfThen f) const {
  346. return call_if_statement<ElseIfPred, ElseIfThen>(f);
  347. }
  348. /**
  349. Specify an else-if-branch functor template (using a nullary boolen
  350. meta-function).
  351. @param f Else-if-branch nullary functor template.
  352. The functor template call @c f() is actually compiled and
  353. executed if and only if @c ElseIfPred::value is @c true (because
  354. the if-statement static predicate is already @c false for this
  355. template specialization).
  356. The return type of @c f() must be the same as (or implicitly
  357. convertible to) the return type of all other functor template
  358. calls specified for this call-if object.
  359. @tparam ElseIfPred Nullary boolean meta-function selecting which functor
  360. template call to compile and execute.
  361. @return A call-if statement so the else statement and additional else-if
  362. statements can be specified if needed.
  363. Eventually, this will be the return value of the functor template
  364. call being compiled and executed.
  365. */
  366. template<class ElseIfPred, typename ElseIfThen>
  367. call_if_statement<ElseIfPred::value, ElseIfThen> else_if(ElseIfThen f)
  368. const {
  369. return call_if_statement<ElseIfPred::value, ElseIfThen>(f);
  370. }
  371. };
  372. /**
  373. Select compilation and execution of functor template calls using a static
  374. boolean predicate.
  375. Create a call-if object with the specified then-branch functor template:
  376. @code
  377. boost::contract::call_if_c<Pred1>(
  378. then_functor_template1
  379. ).template else_if_c<Pred2>( // Optional.
  380. then_functor_template2
  381. ) // Optionally, other `else_if_c` or
  382. ... // `else_if`.
  383. .else_( // Optional for `void` functors,
  384. else_functor_template // but required for non `void`.
  385. )
  386. @endcode
  387. Optional functor templates for else-if-branches and the else-branch can be
  388. specified as needed (the else-branch function template is required if @c f
  389. returns non-void).
  390. @see @RefSect{extras.assertion_requirements__templates_,
  391. Assertion Requirements}
  392. @param f Then-branch nullary functor template.
  393. The functor template call @c f() is compiled and executed if and
  394. only if @c Pred is @c true.
  395. The return type of other functor template calls specified for this
  396. call-if statement (else-branch, else-if-branches, etc.) must be the
  397. same as (or implicitly convertible to) the return type of
  398. then-branch functor call @c f().
  399. @tparam Pred Static boolean predicate selecting which functor template call
  400. to compile and execute.
  401. @return A call-if statement so else and else-if statements can be specified if
  402. needed.
  403. Eventually, this will be the return value of the functor template call
  404. being compiled and executed (which can also be @c void).
  405. */
  406. template<bool Pred, typename Then>
  407. call_if_statement<Pred, Then> call_if_c(Then f) {
  408. return call_if_statement<Pred, Then>(f);
  409. }
  410. /**
  411. Select compilation and execution of functor template calls using a nullary
  412. boolean meta-function.
  413. This is equivalent to <c>boost::contract::call_if_c<Pred::value>(f)</c>.
  414. Create a call-if object with the specified then-branch functor template:
  415. @code
  416. boost::contract::call_if<Pred1>(
  417. then_functor_template1
  418. ).template else_if<Pred2>( // Optional.
  419. then_functor_template2
  420. ) // Optionally, other `else_if` or
  421. ... // `else_if_c`.
  422. .else_( // Optional for `void` functors,
  423. else_functor_template // but required for non `void`.
  424. )
  425. @endcode
  426. Optional functor templates for else-if-branches and the else-branch can be
  427. specified as needed (the else-branch functor template is required if @c f
  428. returns non-void).
  429. @see @RefSect{extras.assertion_requirements__templates_,
  430. Assertion Requirements}
  431. @param f Then-branch nullary functor template.
  432. The functor template call @c f() is compiled and executed if and
  433. only if @c Pred::value is @c true.
  434. The return type of other functor template calls specified for this
  435. call-if statement (else-branch, else-if-branches, etc.) must be the
  436. same as (or implicitly convertible to) the return type of
  437. then-branch functor template call @c f().
  438. @tparam Pred Nullary boolean meta-function selecting which functor template
  439. call to compile and execute.
  440. @return A call-if statement so else and else-if statements can be specified if
  441. needed.
  442. Eventually, this will be the return value of the functor template call
  443. being compiled and executed (which can also be @c void).
  444. */
  445. template<class Pred, typename Then>
  446. call_if_statement<Pred::value, Then> call_if(Then f) {
  447. return call_if_statement<Pred::value, Then>(f);
  448. }
  449. /**
  450. Select compilation and execution of a boolean functor template condition using a
  451. static boolean predicate.
  452. Compile and execute the nullary boolean functor template call @c f() if and only
  453. if the specified static boolean predicate @p Pred is @c true, otherwise
  454. trivially return @p else_ (@c true by default) at run-time.
  455. A call to <c>boost::contract::condition_if_c<Pred>(f, else_)</c> is logically
  456. equivalent to
  457. <c>boost::contract::call_if_c<Pred>(f, [else_] { return else_; })</c> (but
  458. its internal implementation is optimized and it does not actually use
  459. @c call_if_c).
  460. @see @RefSect{extras.assertion_requirements__templates_,
  461. Assertion Requirements}
  462. @param f Nullary boolean functor template.
  463. The functor template call @c f() is compiled and executed if and
  464. only if @c Pred is @c true.
  465. @tparam Pred Static boolean predicate selecting when the functor template
  466. call @c f() should be compiled and executed.
  467. @param else_ Boolean value to return when @c Pred is @c false (instead of
  468. compiling and executing the functor template call @c f()).
  469. @return Boolean value returned by @c f() if the static predicate @c Pred is
  470. @c true. Otherwise, trivially return @p else_.
  471. */
  472. #ifdef BOOST_CONTRACT_DETAIL_DOXYGEN
  473. template<bool Pred, typename Then>
  474. bool condition_if_c(Then f, bool else_ = true);
  475. #else
  476. // NOTE: condition_if is a very simple special case of call_if so it can be
  477. // trivially implemented using enable_if instead of call_if as done below.
  478. template<bool Pred, typename Then>
  479. typename boost::enable_if_c<Pred, bool>::type
  480. condition_if_c(Then f, bool else_ = true) { return f(); }
  481. template<bool Pred, typename Then>
  482. typename boost::disable_if_c<Pred, bool>::type
  483. condition_if_c(Then f, bool else_ = true) { return else_; }
  484. #endif
  485. /**
  486. Select compilation and execution of a boolean functor template condition using a
  487. nullary boolean meta-function.
  488. This is equivalent to
  489. <c>boost::contract::condition_if_c<Pred::value>(f, else_)</c>.
  490. Compile and execute the nullary boolean functor template call @c f() if and only
  491. if the specified nullary boolean meta-function @p Pred is @c true, otherwise
  492. trivially return @p else_ (@c true by default) at run-time.
  493. @see @RefSect{extras.assertion_requirements__templates_,
  494. Assertion Requirements}
  495. @param f Nullary boolean functor template.
  496. The functor template call @c f() is compiled and executed if and
  497. only if @c Pred::value is @c true.
  498. @param else_ Boolean value to return when @c Pred::value is @c false (instead
  499. of compiling and executing the functor template call @c f()).
  500. @tparam Pred Nullary boolean meta-function selecting when the functor
  501. template call @c f() should be compiled and executed.
  502. @return Boolean value returned by @c f() if the static predicate @c Pred is
  503. @c true. Otherwise, trivially return @p else_.
  504. */
  505. template<class Pred, typename Then>
  506. bool condition_if(Then f, bool else_ = true) {
  507. return condition_if_c<Pred::value>(f, else_);
  508. }
  509. } } // namespace
  510. #endif // #include guard