// // require.hpp // gameutils // // Created by Sam Jaffe on 8/19/16. // #pragma once #include #undef CONCAT2 #define CONCAT2(A, B) A##B #undef CONCAT #define CONCAT(A, B) CONCAT2(A, B) #undef STRING2 #define STRING2(A) #A #undef STRING #define STRING(A) STRING2(A) /* expands to the first argument */ #define FIRST(...) FIRST_HELPER(__VA_ARGS__, throwaway) #define FIRST_HELPER(first, ...) first #define NUM(...) SELECT_5TH(__VA_ARGS__, FOURPLUS, THREE, TWO, ONE, throwaway) #define SELECT_5TH(a1, a2, a3, a4, a5, ...) a5 namespace contract { template class ensure_t { private: std::function passes_; std::string message_; public: template ensure_t(F && passes, std::string const & msg, char const * = "") : passes_(passes), message_(msg) {} ~ensure_t() noexcept(false) { if (!std::uncaught_exception() && !passes_()) { throw expect{message_}; } } }; template void _contract_impl(bool expr, std::string const & msg, char const * = "") { if ( ! expr ) throw except{ msg }; } template void _contract_impl(bool expr, char const * msg, char const * = "") { if ( ! expr ) throw except{ msg }; } } #define EXCEPT_T_TWO(X, Y) Y #define EXCEPT_T_THREE(X, Y, Z) Z #define EXCEPT_T_FOURPLUS(X, Y, Z, ...) Y #define EXCEPT_T(...) EXCEPT_T_HELPER(NUM(__VA_ARGS__), __VA_ARGS__) #define EXCEPT_T_HELPER(N, ...) EXCEPT_T_HELPER2(N, __VA_ARGS__) #define EXCEPT_T_HELPER2(N, ...) EXCEPT_T_##N(__VA_ARGS__) #define EXCEPT_MSG_TWO(X, Y) Y #define EXCEPT_MSG_THREE(X, Y, Z) Y #define EXCEPT_MSG_FOURPLUS(X, Y, Z, ...) Z #define EXCEPT_MSG(...) EXCEPT_MSG_HELPER(NUM(__VA_ARGS__), __VA_ARGS__) #define EXCEPT_MSG_HELPER(N, ...) EXCEPT_MSG_HELPER2(N, __VA_ARGS__) #define EXCEPT_MSG_HELPER2(N, ...) EXCEPT_MSG_##N(__VA_ARGS__) #if defined( __clang__ ) || defined( __GNUC__ ) # define FUNCTION STRING(__PRETTY_FUNCTION__) #elif defined( _MSC_VER ) # define FUNCTION STRING(__FUNCTION__) #else # define FUNCTION "???" #endif #define LOCATION_MSG ". in " FUNCTION "(" __FILE__ ":" STRING( __LINE__ ) ")" #define DEF_MSG( header, expr ) \ header " failed: " STRING(expr) LOCATION_MSG #define SUB_MSG( header, bool_expr, rval_expr ) \ header " failed: " STRING(bool_expr) " [ with _ = " STRING(rval_expr) " ]" LOCATION_MSG /* * Usage: * expects( bool-expr ) * expects( bool-expr, custom_msg ) * expects( bool-expr, error_type, custom_msg ) */ #define expects( ... ) \ contract::_contract_impl( \ FIRST(__VA_ARGS__), \ EXCEPT_MSG(__VA_ARGS__, \ DEF_MSG("precondition", FIRST(__VA_ARGS__))) \ ) /* * Usage: * expects_graceful( bool-expr[, rval] ) * * rval - The value to return if the expression is false. * Skip rval for a function returning void. */ #define expects_graceful( expr, ... ) \ if ( ! bool(expr) ) { return __VA_ARGS__; } /* * Usage: * ensures( bool-expr ) * ensures( bool-expr, custom_msg ) * ensures( bool-expr, error_type, custom_msg ) */ #define ensures( ... ) \ contract::ensure_t\ CONCAT(contract_ensures_, __LINE__)( \ [&]() { return FIRST(__VA_ARGS__); }, \ EXCEPT_MSG(__VA_ARGS__ , \ DEF_MSG("postcondition", FIRST(__VA_ARGS__))) \ ) /** * RVO works if expr is an rvalue, but not if it is an * lvalue. e.g: * return ensures( expression, condition ); * will rvo the object (if expression is rvo-able) * but * return ensures( var, condition ); * will not, instead copies * By making _ an argument, both options will move * * Usage: * return_ensures( rval-expr, ensure-expr ) * return_ensures( rval-expr, ensure-expr, custom_msg ) * return_ensures( rval-expr, ensure-expr, error_type, custom_msg ) * * rval-expr - An expression that can be cast to the return type * of the invoking function. Bound to the token '_'. * ensure-expr - A special boolean expression using the token '_'. */ #if __cplusplus < 201300 #define return_ensures( expr, ... ) \ [ & ]( ) { \ decltype((expr)) _ = expr; \ contract::_contract_impl( \ FIRST(__VA_ARGS__), \ EXCEPT_MSG(__VA_ARGS__, \ SUB_MSG("postcondition", FIRST(__VA_ARGS__), expr))); \ return _; \ }( ) #else #define return_ensures( expr, cond, ... ) \ [ _ = expr ]( ) { \ contract::_contract_impl( \ cond, \ EXCEPT_MSG(cond, ##__VA_ARGS__, \ SUB_MSG("postcondition", cond, expr))); \ return _; \ }( ) #endif