// // 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) #undef GET_MACRO_3 #define GET_MACRO_3(_1,_2,_3,NAME,...) NAME namespace contract { 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 FIRST_ELEMENT( X, ... ) X #define SECOND_ELEMENT( X, Y, ... ) Y #define exception_type( ... ) GET_MACRO_3( __VA_ARGS__, SECOND_ELEMENT, FIRST_ELEMENT, FIRST_ELEMENT )( __VA_ARGS__ ) #define IGNORE( ... ) #define COMMA_FIRST_ELEMENT( X, ... ) , X #define COMMA_SECOND_ELEMENT( X, Y, ... ) , Y #define pop_exception( ... ) GET_MACRO_3( "", ##__VA_ARGS__, COMMA_SECOND_ELEMENT, COMMA_FIRST_ELEMENT, IGNORE )( __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 get_default_message( header, expr ) \ header #expr ". in " FUNCTION "(" __FILE__ ":" STRING( __LINE__ ) ")" #define get_subs_message( header, bool_expr, rval_expr ) \ header #bool_expr " [ with _ = " #rval_expr " ]. in " FUNCTION "(" __FILE__ ":" STRING( __LINE__ ) ")" /* * Usage: * expects( bool-expr ) * expects( bool-expr, custom_msg ) * expects( bool-expr, error_type, custom_msg ) */ #define expects( expr, ... ) \ contract::_contract_impl< exception_type( std::logic_error, ##__VA_ARGS__ ) >( \ expr pop_exception( __VA_ARGS__ ), \ get_default_message( "precondition failed: ", expr ) ) /* * 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( cond, ... ) \ contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ )>( \ cond pop_exception( __VA_ARGS__ ) , \ get_default_message( "postcondition failed: ", cond ) ) /** * 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, cond, ... ) \ [ & ]( ) { \ decltype((expr)) _ = expr; \ contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ ) >( \ cond pop_exception( __VA_ARGS__ ) , \ get_subs_message( "postcondition failed: ", cond, expr ) ); \ return _; \ }( ) #else #define return_ensures( expr, cond, ... ) \ [ _ = expr ]( ) { \ contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ ) >( \ cond pop_exception( __VA_ARGS__ ) , \ get_subs_message( "postcondition failed: ", cond, expr ) ); \ return _; \ }( ) #endif