ソースを参照

Adding usage documentation for expect/ensure macro.
Renaming alternate ensure concept as return_ensure. Uses lambda bindings in c++14.
Add expects_graceful, which is for cases where a function should simply return some error value instead of throwing an exception.

Samuel Jaffe 9 年 前
コミット
a6c21a84ea
3 ファイル変更234 行追加50 行削除
  1. 48 6
      expect.hpp
  2. 111 0
      expect.t.h
  3. 75 44
      expect.xcodeproj/project.pbxproj

+ 48 - 6
expect.hpp

@@ -57,18 +57,42 @@ namespace contract {
 #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 ) )
 
-#if ! defined( EXPECT_USE_ENSURE_AUTORETURN_BEHAVIOR )
+/*
+ * 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 , ##__VA_ARGS__ , \
+    cond pop_exception( __VA_ARGS__ ) , \
     get_default_message( "postcondition failed: ", cond ) )
 
-#else
+
 /**
  * RVO works if expr is an rvalue, but not if it is an
  * lvalue. e.g:
@@ -78,13 +102,31 @@ namespace contract {
  * 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 '_'.
  */
-#define ensures( expr, cond, ... ) \
+#if __cplusplus < 201300
+#define return_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 ) ); \
+        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

+ 111 - 0
expect.t.h

@@ -0,0 +1,111 @@
+//
+//  expect.t.h
+//  expect
+//
+//  Created by Sam Jaffe on 1/10/17.
+//
+
+#pragma once
+
+#include <cxxtest/TestSuite.h>
+
+#include "expect.hpp"
+
+struct my_error : public std::logic_error {
+  using std::logic_error::logic_error;
+};
+
+class expect_TestSuite : public CxxTest::TestSuite {
+public:
+  void test_expect_simple( ) const {
+    int i = 1;
+    expects( i == 1 );
+  }
+  
+  void test_expect_fails( ) const {
+    int i = 1;
+    TS_ASSERT_THROWS( expects( i == 2 ), std::logic_error );
+  }
+  
+  void test_expect_fails_with_error_type( ) const {
+    int i = 1;
+    TS_ASSERT_THROWS( (expects( i == 2, my_error, "error" )), my_error );
+  }
+  
+  void test_expect_fails_with_error_message( ) const {
+    int i = 1;
+    try {
+      expects( i == 2 );
+      TS_FAIL( "expected exception" );
+    } catch ( std::exception const & e ) {
+      std::string emsg = e.what();
+      emsg = emsg.substr( 0, emsg.find(".") );
+      TS_ASSERT_EQUALS( emsg, "precondition failed: i == 2" );
+    }
+  }
+  
+  void test_expect_fails_with_custom_error_message( ) const {
+    int i = 1;
+    try {
+      expects( i == 2, "example error message" );
+      TS_FAIL( "expected exception" );
+    } catch ( std::exception const & e ) {
+      TS_ASSERT_EQUALS( e.what(), "example error message" );
+    }
+  }
+  
+  void test_expect_graceful( ) const {
+    int i = 1;
+    expects_graceful( i == 2 );
+    TS_FAIL( "expected return" );
+  }
+  
+  int impl_expect_graceful_rval( ) const {
+    int i = 1;
+    expects_graceful( i == 2, 7 );
+    return 1;
+  }
+  
+  void test_expect_graceful_rval( ) const {
+    TS_ASSERT_EQUALS( impl_expect_graceful_rval(), 7 );
+  }
+  
+  void test_return_ensure( ) const {
+    int i = 6;
+    TS_ASSERT_EQUALS( return_ensures( i + 1, _ > 5 ), 7 );
+  }
+
+  void test_return_ensure_throws( ) const {
+    TS_ASSERT_THROWS( return_ensures( 7, _ < 5 ), std::runtime_error );
+  }
+  
+  int impl_return_ensure_throws_message( ) const {
+    int i = 6;
+    return return_ensures( i + 1, _ < 5 );
+  }
+
+  void test_return_ensure_throws_message( ) const {
+    try {
+      (void) impl_return_ensure_throws_message();
+      TS_FAIL( "expected exception" );
+    } catch ( std::exception const & e ) {
+      std::string emsg = e.what();
+      emsg = emsg.substr( 0, emsg.find("].") + 1 );
+      TS_ASSERT_EQUALS( emsg, "postcondition failed: _ < 5 [ with _ = i + 1 ]")
+    }
+  }
+  
+  void test_return_ensure_throws_custom_message( ) const {
+    try {
+      (void) return_ensures( 7, _ < 5, "example error message" );
+      TS_FAIL( "expected exception" );
+    } catch ( std::exception const & e ) {
+      TS_ASSERT_EQUALS( e.what(), "example error message")
+    }
+  }
+  
+  void test_return_ensure_throws_custom( ) const {
+    TS_ASSERT_THROWS( return_ensures( 7, _ < 5, my_error, "error" ), my_error );
+  }
+
+};

+ 75 - 44
expect.xcodeproj/project.pbxproj

@@ -7,16 +7,30 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		CD2973D31D7B684400E37217 /* expect.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD2973D21D7B684400E37217 /* expect.hpp */; };
+		CD3A75F51E2597A8008C6DC2 /* expect_tc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3A75F31E2597A8008C6DC2 /* expect_tc.cpp */; };
 /* End PBXBuildFile section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+		CD3A75E91E259748008C6DC2 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
-		CD2973C41D7B67EF00E37217 /* libexpect.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libexpect.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD2973D21D7B684400E37217 /* expect.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = expect.hpp; sourceTree = "<group>"; };
+		CD3A75E51E2596B9008C6DC2 /* expect.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = expect.t.h; sourceTree = "<group>"; };
+		CD3A75EB1E259748008C6DC2 /* expect_tc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = expect_tc; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD3A75F31E2597A8008C6DC2 /* expect_tc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = expect_tc.cpp; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
-		CD2973C11D7B67EF00E37217 /* Frameworks */ = {
+		CD3A75E81E259748008C6DC2 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -29,7 +43,8 @@
 		CD2973BB1D7B67EF00E37217 = {
 			isa = PBXGroup;
 			children = (
-				CD2973D21D7B684400E37217 /* expect.hpp */,
+				CD3A75F71E25B2FF008C6DC2 /* src */,
+				CD3A75F61E25B2FB008C6DC2 /* test */,
 				CD2973C51D7B67EF00E37217 /* Products */,
 			);
 			sourceTree = "<group>";
@@ -37,41 +52,48 @@
 		CD2973C51D7B67EF00E37217 /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				CD2973C41D7B67EF00E37217 /* libexpect.dylib */,
+				CD3A75EB1E259748008C6DC2 /* expect_tc */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
-/* End PBXGroup section */
-
-/* Begin PBXHeadersBuildPhase section */
-		CD2973C21D7B67EF00E37217 /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				CD2973D31D7B684400E37217 /* expect.hpp in Headers */,
+		CD3A75F61E25B2FB008C6DC2 /* test */ = {
+			isa = PBXGroup;
+			children = (
+				CD3A75E51E2596B9008C6DC2 /* expect.t.h */,
+				CD3A75F31E2597A8008C6DC2 /* expect_tc.cpp */,
 			);
-			runOnlyForDeploymentPostprocessing = 0;
+			name = test;
+			sourceTree = "<group>";
 		};
-/* End PBXHeadersBuildPhase section */
+		CD3A75F71E25B2FF008C6DC2 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				CD2973D21D7B684400E37217 /* expect.hpp */,
+			);
+			name = src;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		CD2973C31D7B67EF00E37217 /* expect */ = {
+		CD3A75EA1E259748008C6DC2 /* expect_tc */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = CD2973CF1D7B67EF00E37217 /* Build configuration list for PBXNativeTarget "expect" */;
+			buildConfigurationList = CD3A75EF1E259748008C6DC2 /* Build configuration list for PBXNativeTarget "expect_tc" */;
 			buildPhases = (
-				CD2973C01D7B67EF00E37217 /* Sources */,
-				CD2973C11D7B67EF00E37217 /* Frameworks */,
-				CD2973C21D7B67EF00E37217 /* Headers */,
+				CD3A75F21E259771008C6DC2 /* ShellScript */,
+				CD3A75E71E259748008C6DC2 /* Sources */,
+				CD3A75E81E259748008C6DC2 /* Frameworks */,
+				CD3A75E91E259748008C6DC2 /* CopyFiles */,
 			);
 			buildRules = (
 			);
 			dependencies = (
 			);
-			name = expect;
-			productName = expect;
-			productReference = CD2973C41D7B67EF00E37217 /* libexpect.dylib */;
-			productType = "com.apple.product-type.library.dynamic";
+			name = expect_tc;
+			productName = expect_tc;
+			productReference = CD3A75EB1E259748008C6DC2 /* expect_tc */;
+			productType = "com.apple.product-type.tool";
 		};
 /* End PBXNativeTarget section */
 
@@ -82,7 +104,7 @@
 				LastUpgradeCheck = 0720;
 				ORGANIZATIONNAME = "Sam Jaffe";
 				TargetAttributes = {
-					CD2973C31D7B67EF00E37217 = {
+					CD3A75EA1E259748008C6DC2 = {
 						CreatedOnToolsVersion = 7.2.1;
 					};
 				};
@@ -99,16 +121,35 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				CD2973C31D7B67EF00E37217 /* expect */,
+				CD3A75EA1E259748008C6DC2 /* expect_tc */,
 			);
 		};
 /* End PBXProject section */
 
+/* Begin PBXShellScriptBuildPhase section */
+		CD3A75F21E259771008C6DC2 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(SRCROOT)/expect.t.h",
+			);
+			outputPaths = (
+				"$(SRCROOT)/expect_tc.cpp",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "cxxtestgen --error-printer -o expect_tc.cpp expect.t.h";
+		};
+/* End PBXShellScriptBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
-		CD2973C01D7B67EF00E37217 /* Sources */ = {
+		CD3A75E71E259748008C6DC2 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD3A75F51E2597A8008C6DC2 /* expect_tc.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -194,28 +235,18 @@
 			};
 			name = Release;
 		};
-		CD2973D01D7B67EF00E37217 /* Debug */ = {
+		CD3A75F01E259748008C6DC2 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				HEADER_SEARCH_PATHS = /usr/local/include/;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};
 			name = Debug;
 		};
-		CD2973D11D7B67EF00E37217 /* Release */ = {
+		CD3A75F11E259748008C6DC2 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				HEADER_SEARCH_PATHS = /usr/local/include/;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 			};
 			name = Release;
@@ -232,11 +263,11 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CD2973CF1D7B67EF00E37217 /* Build configuration list for PBXNativeTarget "expect" */ = {
+		CD3A75EF1E259748008C6DC2 /* Build configuration list for PBXNativeTarget "expect_tc" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CD2973D01D7B67EF00E37217 /* Debug */,
-				CD2973D11D7B67EF00E37217 /* Release */,
+				CD3A75F01E259748008C6DC2 /* Debug */,
+				CD3A75F11E259748008C6DC2 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 		};