expect.hpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. //
  2. // require.hpp
  3. // gameutils
  4. //
  5. // Created by Sam Jaffe on 8/19/16.
  6. //
  7. #pragma once
  8. #include <stdexcept>
  9. #undef CONCAT2
  10. #define CONCAT2(A, B) A##B
  11. #undef CONCAT
  12. #define CONCAT(A, B) CONCAT2(A, B)
  13. #undef STRING2
  14. #define STRING2(A) #A
  15. #undef STRING
  16. #define STRING(A) STRING2(A)
  17. /* expands to the first argument */
  18. #define FIRST(...) FIRST_HELPER(__VA_ARGS__, throwaway)
  19. #define FIRST_HELPER(first, ...) first
  20. #define NUM(...) SELECT_5TH(__VA_ARGS__, FOURPLUS, THREE, TWO, ONE, throwaway)
  21. #define SELECT_5TH(a1, a2, a3, a4, a5, ...) a5
  22. namespace contract {
  23. template <typename except>
  24. void _contract_impl(bool expr, std::string const & msg, char const * = "") {
  25. if ( ! expr ) throw except{ msg };
  26. }
  27. template <typename except>
  28. void _contract_impl(bool expr, char const * msg, char const * = "") {
  29. if ( ! expr ) throw except{ msg };
  30. }
  31. }
  32. #define EXCEPT_T_TWO(X, Y) Y
  33. #define EXCEPT_T_THREE(X, Y, Z) Z
  34. #define EXCEPT_T_FOURPLUS(X, Y, Z, ...) Y
  35. #define EXCEPT_T(...) EXCEPT_T_HELPER(NUM(__VA_ARGS__), __VA_ARGS__)
  36. #define EXCEPT_T_HELPER(N, ...) EXCEPT_T_HELPER2(N, __VA_ARGS__)
  37. #define EXCEPT_T_HELPER2(N, ...) EXCEPT_T_##N(__VA_ARGS__)
  38. #define EXCEPT_MSG_TWO(X, Y) Y
  39. #define EXCEPT_MSG_THREE(X, Y, Z) Y
  40. #define EXCEPT_MSG_FOURPLUS(X, Y, Z, ...) Z
  41. #define EXCEPT_MSG(...) EXCEPT_MSG_HELPER(NUM(__VA_ARGS__), __VA_ARGS__)
  42. #define EXCEPT_MSG_HELPER(N, ...) EXCEPT_MSG_HELPER2(N, __VA_ARGS__)
  43. #define EXCEPT_MSG_HELPER2(N, ...) EXCEPT_MSG_##N(__VA_ARGS__)
  44. #if defined( __clang__ ) || defined( __GNUC__ )
  45. # define FUNCTION STRING(__PRETTY_FUNCTION__)
  46. #elif defined( _MSC_VER )
  47. # define FUNCTION STRING(__FUNCTION__)
  48. #else
  49. # define FUNCTION "???"
  50. #endif
  51. #define DEF_MSG( header, expr ) \
  52. header " failed: " STRING(expr) ". in " FUNCTION "(" __FILE__ ":" STRING( __LINE__ ) ")"
  53. #define SUB_MSG( header, bool_expr, rval_expr ) \
  54. header " failed: " STRING(bool_expr) " [ with _ = " STRING(rval_expr) " ]. in " FUNCTION "(" __FILE__ ":" STRING( __LINE__ ) ")"
  55. /*
  56. * Usage:
  57. * expects( bool-expr )
  58. * expects( bool-expr, custom_msg )
  59. * expects( bool-expr, error_type, custom_msg )
  60. */
  61. #define expects( ... ) \
  62. contract::_contract_impl<EXCEPT_T( __VA_ARGS__, std::logic_error)>( \
  63. FIRST(__VA_ARGS__), \
  64. EXCEPT_MSG(__VA_ARGS__, \
  65. DEF_MSG("precondition", FIRST(__VA_ARGS__))) \
  66. )
  67. /*
  68. * Usage:
  69. * expects_graceful( bool-expr[, rval] )
  70. *
  71. * rval - The value to return if the expression is false.
  72. * Skip rval for a function returning void.
  73. */
  74. #define expects_graceful( expr, ... ) \
  75. if ( ! bool(expr) ) { return __VA_ARGS__; }
  76. /*
  77. * Usage:
  78. * ensures( bool-expr )
  79. * ensures( bool-expr, custom_msg )
  80. * ensures( bool-expr, error_type, custom_msg )
  81. */
  82. #define ensures( ... ) \
  83. contract::_contract_impl<EXCEPT_T(__VA_ARGS__, std::runtime_error)>( \
  84. FIRST(__VA_ARGS__), \
  85. EXCEPT_MSG(__VA_ARGS__ , \
  86. DEF_MSG("postcondition", FIRST(__VA_ARGS__))) \
  87. )
  88. /**
  89. * RVO works if expr is an rvalue, but not if it is an
  90. * lvalue. e.g:
  91. * return ensures( expression, condition );
  92. * will rvo the object (if expression is rvo-able)
  93. * but
  94. * return ensures( var, condition );
  95. * will not, instead copies
  96. * By making _ an argument, both options will move
  97. *
  98. * Usage:
  99. * return_ensures( rval-expr, ensure-expr )
  100. * return_ensures( rval-expr, ensure-expr, custom_msg )
  101. * return_ensures( rval-expr, ensure-expr, error_type, custom_msg )
  102. *
  103. * rval-expr - An expression that can be cast to the return type
  104. * of the invoking function. Bound to the token '_'.
  105. * ensure-expr - A special boolean expression using the token '_'.
  106. */
  107. #if __cplusplus < 201300
  108. #define return_ensures( expr, ... ) \
  109. [ & ]( ) { \
  110. decltype((expr)) _ = expr; \
  111. contract::_contract_impl<EXCEPT_T(__VA_ARGS__, std::runtime_error)>( \
  112. FIRST(__VA_ARGS__), \
  113. EXCEPT_MSG(__VA_ARGS__, \
  114. SUB_MSG("postcondition", FIRST(__VA_ARGS__), expr))); \
  115. return _; \
  116. }( )
  117. #else
  118. #define return_ensures( expr, cond, ... ) \
  119. [ _ = expr ]( ) { \
  120. contract::_contract_impl<EXCEPT_T(__VA_ARGS__, std::runtime_error)>( \
  121. FIRST(__VA_ARGS__), \
  122. EXCEPT_MSG(__VA_ARGS__, \
  123. SUB_MSG("postcondition", FIRST(__VA_ARGS__), expr))); \
  124. return _; \
  125. }( )
  126. #endif