Переглянути джерело

Provides the following macros:
expect : validate a condition or throw, allows declarative contractual (precondition) programming
expect( predicate )
expect( predicate, message )
expect( predicate, exception_type, message )

ensures : evaluate the expression to the variable '_'. return it if the condition is met, else throw. (postcondition)
ensures( expression, predicate )
ensures( expression, predicate, message )
ensures( expression, predicate, exception_type, message )

Samuel Jaffe 9 роки тому
коміт
a7b1aa92f3
1 змінених файлів з 86 додано та 0 видалено
  1. 86 0
      expect.hpp

+ 86 - 0
expect.hpp

@@ -0,0 +1,86 @@
+//
+//  require.hpp
+//  gameutils
+//
+//  Created by Sam Jaffe on 8/19/16.
+//
+
+#pragma once
+
+#include <stdexcept>
+
+#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 <typename except>
+  void _contract_impl(bool expr, std::string const & msg, char const * = "") {
+    if ( ! expr ) throw except{ msg };
+  }
+  
+  template <typename except>
+  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 expects( expr, ... ) \
+  contract::_contract_impl< exception_type( std::logic_error, ##__VA_ARGS__ ) >( \
+    expr pop_exception( __VA_ARGS__ ), \
+    get_default_message( "precondition failed: ", expr ) )
+
+//#define ensures( cond, ... ) \
+  contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ )>( \
+    cond , ##__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
+#define ensures( expr, cond, ... ) \
+  [ & ]( ) { \
+    decltype((expr)) _ = expr; \
+    contract::_contract_impl< exception_type( std::runtime_error, ##__VA_ARGS__ ) >( \
+        cond , ##__VA_ARGS__ , \
+        get_default_message( "postcondition failed: ", cond ) ); \
+    return _; \
+  }( )
+