expect.hpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. #undef GET_MACRO_3
  18. #define GET_MACRO_3(_1,_2,_3,NAME,...) NAME
  19. namespace contract {
  20. template <typename except>
  21. void _contract_impl(bool expr, std::string const & msg, char const * = "") {
  22. if ( ! expr ) throw except{ msg };
  23. }
  24. template <typename except>
  25. void _contract_impl(bool expr, char const * msg, char const * = "") {
  26. if ( ! expr ) throw except{ msg };
  27. }
  28. }
  29. #define FIRST_ELEMENT( X, ... ) X
  30. #define SECOND_ELEMENT( X, Y, ... ) Y
  31. #define exception_type( ... ) GET_MACRO_3( __VA_ARGS__, SECOND_ELEMENT, FIRST_ELEMENT, FIRST_ELEMENT )( __VA_ARGS__ )
  32. #define IGNORE( ... )
  33. #define COMMA_FIRST_ELEMENT( X, ... ) , X
  34. #define COMMA_SECOND_ELEMENT( X, Y, ... ) , Y
  35. #define pop_exception( ... ) GET_MACRO_3( "", ##__VA_ARGS__, COMMA_SECOND_ELEMENT, COMMA_FIRST_ELEMENT, IGNORE )( __VA_ARGS__ )
  36. #if defined( __clang__ ) || defined( __GNUC__ )
  37. # define FUNCTION STRING(__PRETTY_FUNCTION__)
  38. #elif defined( _MSC_VER )
  39. # define FUNCTION STRING(__FUNCTION__)
  40. #else
  41. # define FUNCTION "???"
  42. #endif
  43. #define get_default_message( header, expr ) \
  44. header #expr ". in " FUNCTION "(" __FILE__ ":" STRING( __LINE__ ) ")"
  45. #define get_subs_message( header, bool_expr, rval_expr ) \
  46. header #bool_expr " [ with _ = " #rval_expr " ]. in " FUNCTION "(" __FILE__ ":" STRING( __LINE__ ) ")"
  47. /*
  48. * Usage:
  49. * expects( bool-expr )
  50. * expects( bool-expr, custom_msg )
  51. * expects( bool-expr, error_type, custom_msg )
  52. */
  53. #define expects( expr, ... ) \
  54. contract::_contract_impl< exception_type( std::logic_error, ##__VA_ARGS__ ) >( \
  55. expr pop_exception( __VA_ARGS__ ), \
  56. get_default_message( "precondition failed: ", expr ) )
  57. /*
  58. * Usage:
  59. * expects_graceful( bool-expr[, rval] )
  60. *
  61. * rval - The value to return if the expression is false.
  62. * Skip rval for a function returning void.
  63. */
  64. #define expects_graceful( expr, ... ) \
  65. if ( ! bool(expr) ) { return __VA_ARGS__; }
  66. /*
  67. * Usage:
  68. * ensures( bool-expr )
  69. * ensures( bool-expr, custom_msg )
  70. * ensures( bool-expr, error_type, custom_msg )
  71. */
  72. #define ensures( cond, ... ) \
  73. contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ )>( \
  74. cond pop_exception( __VA_ARGS__ ) , \
  75. get_default_message( "postcondition failed: ", cond ) )
  76. /**
  77. * RVO works if expr is an rvalue, but not if it is an
  78. * lvalue. e.g:
  79. * return ensures( expression, condition );
  80. * will rvo the object (if expression is rvo-able)
  81. * but
  82. * return ensures( var, condition );
  83. * will not, instead copies
  84. * By making _ an argument, both options will move
  85. *
  86. * Usage:
  87. * return_ensures( rval-expr, ensure-expr )
  88. * return_ensures( rval-expr, ensure-expr, custom_msg )
  89. * return_ensures( rval-expr, ensure-expr, error_type, custom_msg )
  90. *
  91. * rval-expr - An expression that can be cast to the return type
  92. * of the invoking function. Bound to the token '_'.
  93. * ensure-expr - A special boolean expression using the token '_'.
  94. */
  95. #if __cplusplus < 201300
  96. #define return_ensures( expr, cond, ... ) \
  97. [ & ]( ) { \
  98. decltype((expr)) _ = expr; \
  99. contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ ) >( \
  100. cond pop_exception( __VA_ARGS__ ) , \
  101. get_subs_message( "postcondition failed: ", cond, expr ) ); \
  102. return _; \
  103. }( )
  104. #else
  105. #define return_ensures( expr, cond, ... ) \
  106. [ _ = expr ]( ) { \
  107. contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ ) >( \
  108. cond pop_exception( __VA_ARGS__ ) , \
  109. get_subs_message( "postcondition failed: ", cond, expr ) ); \
  110. return _; \
  111. }( )
  112. #endif