Pārlūkot izejas kodu

Merge branch 'arithmetic'

* arithmetic:
  Fix spelling error Arithmatic -> Arithmetic
  Test all other current arithmetic operations.
  Add a test for merged Incrementable/Decrementable
  Test increment and decrement.
  Fix compiler bug in Incrementable/Decrementable.
  Re-order test files to be cleaner, eliminate most using declarations.
  Better name, less excess
  Build out non-arithmatic tests, including splitting files.
  Fix compiler error that occurs due to include order
  Add skills for +, -, unary -, *, /, ++, and --.
Sam Jaffe 5 gadi atpakaļ
vecāks
revīzija
f4233e2a03

+ 56 - 0
include/opaque_typedef/arithmetic.hpp

@@ -0,0 +1,56 @@
+#pragma once
+
+namespace types {
+  template <typename Self>
+  struct Incrementable {
+    friend Self operator++(Self & self, int) {
+      Self copy = self;
+      self = Self(self.get() + 1);
+      return copy;
+    }
+    friend Self & operator++(Self & self) {
+      return self = Self(self.get() + 1);
+    }
+  };
+
+  template <typename Self>
+  struct Decrementable {
+    friend Self operator--(Self & self, int) {
+      Self copy = self;
+      self = Self(self.get() - 1);
+      return copy;
+    }
+    friend Self & operator--(Self & self) {
+      return self = Self(self.get() - 1);
+    }
+  };
+
+  template <typename Self>
+  struct Addable {
+    friend Self operator+(Self const & lhs, Self const & rhs) {
+      return Self(lhs.get() + rhs.get());
+    }
+  };
+    
+  template <typename Self>
+  struct Arithmetic : Addable<Self> {
+    friend Self operator-(Self const & self) {
+      return Self(-self.get());
+    }
+    
+    friend Self operator-(Self const & lhs, Self const & rhs) {
+      return Self(lhs.get() - rhs.get());
+    }
+  };
+
+  template <typename Self>
+  struct Numeric : Arithmetic<Self> {
+    friend Self operator*(Self const & lhs, Self const & rhs) {
+      return Self(lhs.get() * rhs.get());
+    }
+    
+    friend Self operator/(Self const & lhs, Self const & rhs) {
+      return Self(lhs.get() / rhs.get());
+    }
+  };
+}

+ 4 - 1
include/opaque_typedef/opaque_typedef.hpp

@@ -9,6 +9,9 @@
 
 #pragma once
 
+#include <utility>
+
+#include "arithmetic.hpp"
 #include "comparable.hpp"
 
 namespace types {
@@ -26,5 +29,5 @@ namespace types {
     
     Base const & get() const { return value_; }
     explicit operator Base const &() const { return value_; }
-  };
+  };  
 }

+ 16 - 99
opaque_typedef.xcodeproj/project.pbxproj

@@ -7,9 +7,12 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		CD70491220C48B7C007C944C /* opaque_typedef_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD7048F020C48AE4007C944C /* opaque_typedef_test.cpp */; };
+		CD70491220C48B7C007C944C /* opaque_typedef_conversion_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD7048F020C48AE4007C944C /* opaque_typedef_conversion_test.cxx */; };
 		CD70491520C48B8C007C944C /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD7048FB20C48B30007C944C /* GoogleMock.framework */; };
 		CDE8546724DF5051006FE7C7 /* opaque_typedef in Resources */ = {isa = PBXBuildFile; fileRef = CDE8546624DF5051006FE7C7 /* opaque_typedef */; };
+		CDE8547324DF80EE006FE7C7 /* opaque_typedef_comparable_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDE8547224DF80EE006FE7C7 /* opaque_typedef_comparable_test.cxx */; };
+		CDE8547524DF8110006FE7C7 /* opaque_typedef_adhoc_arithmetic_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDE8547424DF8110006FE7C7 /* opaque_typedef_adhoc_arithmetic_test.cxx */; };
+		CDE8547724DF8A6A006FE7C7 /* opaque_typedef_arithmetic_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDE8547624DF8A6A006FE7C7 /* opaque_typedef_arithmetic_test.cxx */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -41,13 +44,6 @@
 			remoteGlobalIDString = 05818F901A685AEA0072A469;
 			remoteInfo = GoogleMockTests;
 		};
-		CD70490D20C48B75007C944C /* PBXContainerItemProxy */ = {
-			isa = PBXContainerItemProxy;
-			containerPortal = CD3C80601D63EE0000ACC795 /* Project object */;
-			proxyType = 1;
-			remoteGlobalIDString = CD3C80671D63EE0000ACC795;
-			remoteInfo = opaque_typedef;
-		};
 		CD70491320C48B88007C944C /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */;
@@ -58,22 +54,17 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
-		CD3C80681D63EE0000ACC795 /* libopaque_typedef.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libopaque_typedef.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
-		CD7048F020C48AE4007C944C /* opaque_typedef_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_typedef_test.cpp; sourceTree = "<group>"; };
+		CD7048F020C48AE4007C944C /* opaque_typedef_conversion_test.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_typedef_conversion_test.cxx; sourceTree = "<group>"; };
 		CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 		CD70490720C48B75007C944C /* opaque_typedef_test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = opaque_typedef_test.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD70490B20C48B75007C944C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		CDE8546624DF5051006FE7C7 /* opaque_typedef */ = {isa = PBXFileReference; lastKnownFileType = folder; name = opaque_typedef; path = include/opaque_typedef; sourceTree = "<group>"; };
+		CDE8547224DF80EE006FE7C7 /* opaque_typedef_comparable_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_typedef_comparable_test.cxx; sourceTree = "<group>"; };
+		CDE8547424DF8110006FE7C7 /* opaque_typedef_adhoc_arithmetic_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_typedef_adhoc_arithmetic_test.cxx; sourceTree = "<group>"; };
+		CDE8547624DF8A6A006FE7C7 /* opaque_typedef_arithmetic_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_typedef_arithmetic_test.cxx; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
-		CD3C80651D63EE0000ACC795 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		CD70490420C48B75007C944C /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -99,7 +90,6 @@
 		CD3C80691D63EE0000ACC795 /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				CD3C80681D63EE0000ACC795 /* libopaque_typedef.dylib */,
 				CD70490720C48B75007C944C /* opaque_typedef_test.xctest */,
 			);
 			name = Products;
@@ -108,7 +98,10 @@
 		CD7048F220C48B19007C944C /* test */ = {
 			isa = PBXGroup;
 			children = (
-				CD7048F020C48AE4007C944C /* opaque_typedef_test.cpp */,
+				CD7048F020C48AE4007C944C /* opaque_typedef_conversion_test.cxx */,
+				CDE8547224DF80EE006FE7C7 /* opaque_typedef_comparable_test.cxx */,
+				CDE8547424DF8110006FE7C7 /* opaque_typedef_adhoc_arithmetic_test.cxx */,
+				CDE8547624DF8A6A006FE7C7 /* opaque_typedef_arithmetic_test.cxx */,
 			);
 			path = test;
 			sourceTree = "<group>";
@@ -134,34 +127,7 @@
 		};
 /* End PBXGroup section */
 
-/* Begin PBXHeadersBuildPhase section */
-		CD3C80661D63EE0000ACC795 /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXHeadersBuildPhase section */
-
 /* Begin PBXNativeTarget section */
-		CD3C80671D63EE0000ACC795 /* opaque_typedef */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = CD3C80731D63EE0000ACC795 /* Build configuration list for PBXNativeTarget "opaque_typedef" */;
-			buildPhases = (
-				CD3C80641D63EE0000ACC795 /* Sources */,
-				CD3C80651D63EE0000ACC795 /* Frameworks */,
-				CD3C80661D63EE0000ACC795 /* Headers */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = opaque_typedef;
-			productName = opaque_typedef;
-			productReference = CD3C80681D63EE0000ACC795 /* libopaque_typedef.dylib */;
-			productType = "com.apple.product-type.library.dynamic";
-		};
 		CD70490620C48B75007C944C /* opaque_typedef_test */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = CD70490F20C48B75007C944C /* Build configuration list for PBXNativeTarget "opaque_typedef_test" */;
@@ -174,7 +140,6 @@
 			);
 			dependencies = (
 				CD70491420C48B88007C944C /* PBXTargetDependency */,
-				CD70490E20C48B75007C944C /* PBXTargetDependency */,
 			);
 			name = opaque_typedef_test;
 			productName = opaque_typedef_test;
@@ -190,9 +155,6 @@
 				LastUpgradeCheck = 1030;
 				ORGANIZATIONNAME = "Sam Jaffe";
 				TargetAttributes = {
-					CD3C80671D63EE0000ACC795 = {
-						CreatedOnToolsVersion = 7.2.1;
-					};
 					CD70490620C48B75007C944C = {
 						CreatedOnToolsVersion = 7.2.1;
 					};
@@ -217,7 +179,6 @@
 			);
 			projectRoot = "";
 			targets = (
-				CD3C80671D63EE0000ACC795 /* opaque_typedef */,
 				CD70490620C48B75007C944C /* opaque_typedef_test */,
 			);
 		};
@@ -266,29 +227,20 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
-		CD3C80641D63EE0000ACC795 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		CD70490320C48B75007C944C /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CD70491220C48B7C007C944C /* opaque_typedef_test.cpp in Sources */,
+				CDE8547524DF8110006FE7C7 /* opaque_typedef_adhoc_arithmetic_test.cxx in Sources */,
+				CDE8547324DF80EE006FE7C7 /* opaque_typedef_comparable_test.cxx in Sources */,
+				CDE8547724DF8A6A006FE7C7 /* opaque_typedef_arithmetic_test.cxx in Sources */,
+				CD70491220C48B7C007C944C /* opaque_typedef_conversion_test.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
-		CD70490E20C48B75007C944C /* PBXTargetDependency */ = {
-			isa = PBXTargetDependency;
-			target = CD3C80671D63EE0000ACC795 /* opaque_typedef */;
-			targetProxy = CD70490D20C48B75007C944C /* PBXContainerItemProxy */;
-		};
 		CD70491420C48B88007C944C /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			name = GoogleMock;
@@ -402,32 +354,6 @@
 			};
 			name = Release;
 		};
-		CD3C80741D63EE0000ACC795 /* 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;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Debug;
-		};
-		CD3C80751D63EE0000ACC795 /* 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;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Release;
-		};
 		CD70491020C48B75007C944C /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -462,15 +388,6 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CD3C80731D63EE0000ACC795 /* Build configuration list for PBXNativeTarget "opaque_typedef" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				CD3C80741D63EE0000ACC795 /* Debug */,
-				CD3C80751D63EE0000ACC795 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
 		CD70490F20C48B75007C944C /* Build configuration list for PBXNativeTarget "opaque_typedef_test" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (

+ 53 - 0
test/opaque_typedef_adhoc_arithmetic_test.cxx

@@ -0,0 +1,53 @@
+//
+//  opaque_typedef_arithmatic_test.cpp
+//  opaque_typedef_test
+//
+//  Created by Sam Jaffe on 8/8/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#include <gmock/gmock.h>
+#include <cmath>
+
+#include "opaque_typedef/arithmetic.hpp"
+#include "opaque_typedef/opaque_typedef.hpp"
+
+
+// Allow Eq() checks without needing the Skill EqualityComparable
+template <typename T>
+bool operator==(T const & lhs, T const & rhs) {
+  return lhs.get() == rhs.get();
+}
+
+// Types for Test Cases
+using namespace types;
+using meters = opaque_typedef<double, struct meters_tag>;
+using seconds = opaque_typedef<double, struct seconds_tag>;
+using mps = opaque_typedef<double, struct mps_tag>;
+
+mps operator/(meters m, seconds s) {
+  return mps{m.get() / s.get()};
+}
+
+// Testing Printers
+std::ostream & operator<<(std::ostream & os, meters const & ot) {
+  return os << double(ot) << "m";
+}
+
+std::ostream & operator<<(std::ostream & os, seconds const & ot) {
+  return os << double(ot) << "s";
+}
+
+void PrintTo(mps const & ot, std::ostream * os) {
+  (*os) << double(ot) << "m/s";
+}
+
+// Actual Test Bodies
+TEST(OpaqueTypedefTest, CanDefineExternalFunctionsForTypeMerging) {
+  meters m{10.0};
+  seconds s{0.5};
+
+  EXPECT_TRUE((testing::StaticAssertTypeEq<mps, decltype(m/s)>()));
+  EXPECT_THAT((m/s), testing::Eq(mps(20.0)))
+    << "     lhs: " << m << "\n     rhs: " <<  s;
+}

+ 137 - 0
test/opaque_typedef_arithmetic_test.cxx

@@ -0,0 +1,137 @@
+//
+//  opaque_typedef_arithmatic_test.cpp
+//  opaque_typedef_test
+//
+//  Created by Sam Jaffe on 8/8/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#include <gmock/gmock.h>
+
+#include "opaque_typedef/arithmetic.hpp"
+#include "opaque_typedef/opaque_typedef.hpp"
+
+// Allow Eq() checks without needing the Skill EqualityComparable
+template <typename T>
+bool operator==(T const & lhs, T const & rhs) {
+  return lhs.get() == rhs.get();
+}
+
+// Make compilation (but not linking) possible for StaticAssertTypeEq to check
+// 'does a real version of this function exist'
+template <typename T> void operator++(T, int);
+template <typename T> void operator++(T);
+template <typename T> void operator--(T, int);
+template <typename T> void operator--(T);
+template <typename T> void operator-(T, T);
+template <typename T> void operator-(T);
+template <typename T> void operator*(T, T);
+template <typename T> void operator/(T, T);
+
+// Types for Test Cases
+using namespace types;
+using counter = opaque_typedef<uint32_t, struct counter_tag, Incrementable>;
+using countdown = opaque_typedef<uint32_t, struct countdown_tag, Decrementable>;
+using idx = opaque_typedef<uint32_t, struct idx_tag, Incrementable, Decrementable>;
+using timer = opaque_typedef<double, struct timer_tag, Addable>;
+using price = opaque_typedef<double, struct price_tag, Arithmetic>;
+using shares = opaque_typedef<double, struct shares_tag, Numeric>;
+
+// Actual Test Bodies
+TEST(OpaqueTypedefDecrementableTest, CannotDecrement) {
+  counter c{1};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(c--)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(--c)>()));
+}
+
+TEST(OpaqueTypedefIncrementableTest, CanPostIncrement) {
+  counter c{1};
+  EXPECT_THAT(c++, testing::Eq(counter{1}));
+  EXPECT_THAT(c, testing::Eq(counter{2}));
+}
+
+TEST(OpaqueTypedefIncrementableTest, CanPreIncrement) {
+  counter c{1};
+  EXPECT_THAT(++c, testing::Eq(counter{2}));
+  EXPECT_THAT(c, testing::Eq(counter{2}));
+}
+
+TEST(OpaqueTypedefDecrementableTest, CannotIncrement) {
+  countdown c{10};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(c++)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(++c)>()));
+}
+
+TEST(OpaqueTypedefDecrementableTest, CanPostDecrement) {
+  countdown c{10};
+  EXPECT_THAT(c--, testing::Eq(countdown{10}));
+  EXPECT_THAT(c, testing::Eq(countdown{9}));
+}
+
+TEST(OpaqueTypedefDecrementableTest, CanPreDecrement) {
+  countdown c{10};
+  EXPECT_THAT(--c, testing::Eq(countdown{9}));
+  EXPECT_THAT(c, testing::Eq(countdown{9}));
+}
+
+TEST(OpaqueTypedefTest, CanMergeIncrementAndDecrement) {
+  idx i{1};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<idx, decltype(i--)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<idx&, decltype(--i)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<idx, decltype(i++)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<idx&, decltype(++i)>()));
+}
+
+TEST(OpaqueTypedefAddableTest, CannotSubtractOrNegate) {
+  timer t{5.0};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(t-t)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(-t)>()));
+}
+
+TEST(OpaqueTypedefAddableTest, CanPerformAddition) {
+  timer t{5.0};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<timer, decltype(t+t)>()));
+}
+
+TEST(OpaqueTypedefAddableTest, AddPassesThroughToBase) {
+  timer t{5.0};
+  EXPECT_THAT(t+t, testing::Eq(timer(10.0)));
+}
+
+TEST(OpaqueTypedefArithmeticTest, CannotMultiplyOrDivide) {
+  price p{99.99};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(p*p)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(p/p)>()));
+}
+
+TEST(OpaqueTypedefArithmeticTest, CanAddSubtractAndNegate) {
+  price p{99.99};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<price, decltype(p+p)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<price, decltype(p-p)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<price, decltype(-p)>()));
+}
+
+TEST(OpaqueTypedefArithmeticTest, PassesThroughToBase) {
+  price p{50.0};
+  EXPECT_THAT(p+p, testing::Eq(price(100.0)));
+  EXPECT_THAT(p-p, testing::Eq(price(0.0)));
+  EXPECT_THAT(-p, testing::Eq(price(-50.0)));
+}
+
+TEST(OpaqueTypedefNumericTest, CanPerformAllBinaryOpsAndNegation) {
+  shares s{100.0};
+  EXPECT_TRUE((testing::StaticAssertTypeEq<shares, decltype(s+s)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<shares, decltype(s-s)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<shares, decltype(s*s)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<shares, decltype(s/s)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<shares, decltype(-s)>()));
+}
+
+TEST(OpaqueTypedefNumericTest, PassesThroughToBase) {
+  shares s{100.0};
+  EXPECT_THAT(s+s, testing::Eq(shares(200.0)));
+  EXPECT_THAT(s-s, testing::Eq(shares(0.0)));
+  EXPECT_THAT(s*s, testing::Eq(shares(10000.0)));
+  EXPECT_THAT(s/s, testing::Eq(shares(1.0)));
+  EXPECT_THAT(-s, testing::Eq(shares(-100.0)));
+}

+ 99 - 0
test/opaque_typedef_comparable_test.cxx

@@ -0,0 +1,99 @@
+//
+//  opaque_typedef_comparable_test.cpp
+//  opaque_typedef_test
+//
+//  Created by Sam Jaffe on 8/8/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#include <gmock/gmock.h>
+
+#include "opaque_typedef/comparable.hpp"
+#include "opaque_typedef/opaque_typedef.hpp"
+
+// Make compilation (but not linking) possible for StaticAssertTypeEq to check
+// 'does a real version of this function exist'
+template <typename T> void operator==(T const &, T const &);
+template <typename T> void operator!=(T const &, T const &);
+template <typename T> void operator<(T const &, T const &);
+template <typename T> void operator<=(T const &, T const &);
+template <typename T> void operator>(T const &, T const &);
+template <typename T> void operator>=(T const &, T const &);
+
+// Types for Test Cases
+using namespace types;
+using incomparible = opaque_typedef<int, struct incomparible_tag>;
+using port = opaque_typedef<int, struct port_tag, EqualityComparable>;
+using x_pos = opaque_typedef<int, struct x_pos_tag, Comparable>;
+
+// Testing Printers
+void PrintTo(incomparible const & ot, std::ostream * os) {
+  (*os) << "incomparible";
+}
+
+void PrintTo(port const & ot, std::ostream * os) {
+  (*os) << "port:" << int(ot);
+}
+
+void PrintTo(x_pos const & ot, std::ostream * os) {
+  (*os) << "x:" << int(ot);
+}
+
+// Actual Test Bodies
+TEST(OpaqueTypedefNotComparableTest, CannotCompareAny) {
+  incomparible i(1);
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(i == i)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(i != i)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(i < i)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(i <= i)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(i > i)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(i >= i)>()));
+}
+
+TEST(OpaqueTypedefEqualityComparableTest, CanCheckEq) {
+  port p(80);
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(p == p)>()));
+}
+
+TEST(OpaqueTypedefEqualityComparableTest, CanCheckNotEq) {
+  port p(80);
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(p != p)>()));
+}
+
+TEST(OpaqueTypedefEqualityComparableTest, CannotCompareOrder) {
+  port p(80);
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(p < p)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(p <= p)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(p > p)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<void, decltype(p >= p)>()));
+}
+
+TEST(OpaqueTypedefEqualityComparableTest, EqAndNePassThroughToBaseType) {
+  port http{80};
+  port https{443};
+  EXPECT_THAT(http, testing::Eq(port{80}));
+  EXPECT_THAT(http, testing::Ne(https));
+}
+
+TEST(OpaqueTypedefCompareableTest, CanCompareAll) {
+  x_pos x(0);
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(x == x)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(x != x)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(x < x)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(x <= x)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(x > x)>()));
+  EXPECT_TRUE((testing::StaticAssertTypeEq<bool, decltype(x >= x)>()));
+}
+
+TEST(OpaqueTypedefCompareableTest, ComparePassesThroughToBaseType) {
+  x_pos x1{0};
+  x_pos x2{1};
+  EXPECT_THAT(x1, testing::Eq(x1));
+  EXPECT_THAT(x1, testing::Ne(x2));
+  EXPECT_THAT(x1, testing::Lt(x2));
+  EXPECT_THAT(x1, testing::Le(x2));
+  EXPECT_THAT(x1, testing::Le(x1));
+  EXPECT_THAT(x2, testing::Gt(x1));
+  EXPECT_THAT(x2, testing::Ge(x1));
+  EXPECT_THAT(x1, testing::Ge(x1));
+}

+ 53 - 0
test/opaque_typedef_conversion_test.cxx

@@ -0,0 +1,53 @@
+//
+//  opaque_typedef_test.cpp
+//  opaque_typedef
+//
+//  Created by Sam Jaffe on 6/3/18.
+//
+
+#include <gmock/gmock.h>
+
+#include "opaque_typedef/opaque_typedef.hpp"
+
+// Allow Eq() checks without needing the Skill EqualityComparable
+template <typename T>
+bool operator==(T const & lhs, T const & rhs) {
+  return lhs.get() == rhs.get();
+}
+
+// Types for Test Cases
+using namespace types;
+using degree = opaque_typedef<double, struct degree_tag>;
+using radian = opaque_typedef<double, struct radian_tag>;
+
+template <> template <> radian::opaque_typedef(degree const & other)
+    : radian(other.get() * M_PI_2 / 90.0) {}
+
+template <> template <> degree::opaque_typedef(radian const & other)
+    : degree(other.get() * 90.0 / M_PI_2) {}
+
+// Testing Printers
+void PrintTo(radian const & ot, std::ostream * os) {
+  (*os) << double(ot)/M_PI << "π";
+}
+
+void PrintTo(degree const & ot, std::ostream * os) {
+  (*os) << double(ot) << "°";
+}
+
+// Actual Test Bodies
+TEST(OpaqueTypedefConversionTest, CanCastObjects) {
+  degree deg{90.0};
+  radian rad{M_PI_2};
+  
+  EXPECT_THAT(radian(deg), testing::Eq(rad));
+  EXPECT_THAT(degree(rad), testing::Eq(deg));
+}
+
+TEST(OpaqueTypedefConversionTest, ManualConversionDoesNotScale) {
+  degree deg{90.0};
+  radian rad{M_PI_2};
+  
+  EXPECT_THAT(radian(double(deg)), testing::Not(rad));
+  EXPECT_THAT(degree(double(rad)), testing::Not(deg));
+}

+ 0 - 53
test/opaque_typedef_test.cpp

@@ -1,53 +0,0 @@
-//
-//  opaque_typedef_test.cpp
-//  opaque_typedef
-//
-//  Created by Sam Jaffe on 6/3/18.
-//
-
-#include <gmock/gmock.h>
-#include <cmath>
-
-#include "opaque_typedef/opaque_typedef.hpp"
-
-using degree = types::opaque_typedef<double, struct degree_tag, types::Comparable>;
-using radian = types::opaque_typedef<double, struct radian_tag, types::Comparable>;
-
-template <> template <>radian::opaque_typedef(degree const & other)
-    : radian(other.get() * M_PI_2 / 90.0) {}
-
-template <> template <> degree::opaque_typedef(radian const & other)
-    : degree(other.get() * 90.0 / M_PI_2) {}
-
-void PrintTo(radian const & ot, std::ostream * os) {
-  (*os) << double(ot)/M_PI << "π";
-}
-
-void PrintTo(degree const & ot, std::ostream * os) {
-  (*os) << double(ot) << "°";
-}
-
-TEST(OpaqueTypedefTest, Conversion) {
-  degree deg{90.0};
-  radian rad{M_PI_2};
-  
-  EXPECT_THAT(radian(deg), rad);
-  EXPECT_THAT(degree(rad), deg);
-  EXPECT_THAT(radian(double(deg)), ::testing::Not(rad));
-  EXPECT_THAT(degree(double(rad)), ::testing::Not(deg));
-}
-
-using meters = types::opaque_typedef<double, struct meters_tag, types::Comparable>;
-using seconds = types::opaque_typedef<double, struct seconds_tag, types::Comparable>;
-using meter_per_second = types::opaque_typedef<double, struct mps_tag, types::Comparable>;
-
-meter_per_second operator/(meters m, seconds s) {
-  return meter_per_second{m.get() / s.get()};
-}
-
-TEST(OpaqueTypedefTest, MixedFunctions) {
-  meters m{10.0};
-  seconds s{0.5};
-
-  EXPECT_THAT(m/s, meter_per_second{20.0});
-}