瀏覽代碼

Updating opaque typedef, including adding support for auto-generation of opaque_typedefs with operators, and optional conversion functions.

Sam Jaffe 7 年之前
父節點
當前提交
e52b0ed5bb
共有 4 個文件被更改,包括 351 次插入22 次删除
  1. 63 15
      opaque_typedef.hpp
  2. 221 7
      opaque_typedef.xcodeproj/project.pbxproj
  3. 43 0
      opaque_typedef_test.cpp
  4. 24 0
      opaque_typedef_test/Info.plist

+ 63 - 15
opaque_typedef.hpp

@@ -9,8 +9,12 @@
 
 #pragma once
 
+#include <type_traits>
+
+template <typename, typename> class opaque_typedef;
+
 template <typename Tag, typename T>
-T underlying_type_impl(strong_typedef<Tag, T>);
+T underlying_type_impl(opaque_typedef<Tag, T>);
 
 template <typename T>
 using underlying_type = decltype(underlying_type_impl(std::declval<T>()));
@@ -30,15 +34,18 @@ using underlying_type = decltype(underlying_type_impl(std::declval<T>()));
     } \
   }
 
-#define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
+#define OPAQUE_TYPE_BINARY_OPERATOR_T(name, op, Out) \
   template <typename TDef> \
   struct name { \
-    friend TDef operator op(TDef const & lhs, TDef const & rhs) { \
+    friend Out operator op(TDef const & lhs, TDef const & rhs) { \
       using type = underlying_type<TDef>; \
-      return TDef(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
+      return Out(static_cast<type const&>(lhs) op static_cast<type const&>(rhs)); \
     } \
   }
 
+#define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
+  OPAQUE_TYPE_BINARY_OPERATOR_T(name, op, TDef)
+
 #define OPAQUE_TYPE_MIXED_BINARY_OPERATOR(name, op) \
   template <typename TDef, typename RDef, typename Out> \
   struct name { \
@@ -58,14 +65,14 @@ using underlying_type = decltype(underlying_type_impl(std::declval<T>()));
       return lhs; \
     } \
     \
-    friend Out operator op(TDef const & lhs, RDef const & rhs) { \
-      using type1 = underlying_type<TDef>; \
-      using type2 = underlying_type<RDef>; \
-      return Out(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
+    friend TDef operator op(TDef const & lhs, RDef const & rhs) { \
+      using type1 = std::underlying_type<TDef>; \
+      using type2 = std::underlying_type<RDef>; \
+      return TDef(static_cast<type1 const&>(lhs) op static_cast<type2 const&>(rhs)); \
     } \
   }
 
-#define OPAQUE_TYPE_BINARY_OPERATOR(name, op) \
+#define OPAQUE_TYPE_UNARY_OPERATOR(name, op) \
   template <typename TDef> \
   struct name { \
     friend TDef operator op(TDef const & val) { \
@@ -83,6 +90,13 @@ OPAQUE_TYPE_ASSIGN_OPERATOR(multiplication, *);
 OPAQUE_TYPE_ASSIGN_OPERATOR(division, /);
 OPAQUE_TYPE_ASSIGN_OPERATOR(modulo, %);
 
+OPAQUE_TYPE_BINARY_OPERATOR_T(equals, ==, bool);
+OPAQUE_TYPE_BINARY_OPERATOR_T(not_equals, !=, bool);
+OPAQUE_TYPE_BINARY_OPERATOR_T(less_than, <, bool);
+OPAQUE_TYPE_BINARY_OPERATOR_T(less_equal, <=, bool);
+OPAQUE_TYPE_BINARY_OPERATOR_T(greater_than, >, bool);
+OPAQUE_TYPE_BINARY_OPERATOR_T(greater_equal, >=, bool);
+
 OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_addition, +);
 OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_subtraction, -);
 OPAQUE_TYPE_MIXED_BINARY_OPERATOR(mixed_multiplication, *);
@@ -92,17 +106,24 @@ OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_and, &);
 OPAQUE_TYPE_ASSIGN_OPERATOR(bitwise_or, |);
 OPAQUE_TYPE_ASSIGN_OPERATOR(exclusive_or, ^);
 
-template <typename TDef>
-struct numeric_arithmatic : unary_plus<TDef>, unary_minus<TDef>, addition<TDef>, subtraction<TDef>, multiplication<TDef>, division<TDef> {};
-
-template <typename TDef>
-struct integer_arithmatic : numeric_arithmatic<TDef>, modulo<TDef> {}
-
+#undef OPAQUE_TYPE_BINARY_OPERATOR_T
 #undef OPAQUE_TYPE_BINARY_OPERATOR
 #undef OPAQUE_TYPE_MIXED_BINARY_OPERATOR
 #undef OPAQUE_TYPE_UNARY_OPERATOR
 #undef OPAQUE_TYPE_ASSIGN_OPERATOR
 
+template <typename TDef>
+struct numeric_arithmetic : unary_plus<TDef>, unary_minus<TDef>, addition<TDef>, subtraction<TDef>, multiplication<TDef>, division<TDef> {};
+
+template <typename TDef>
+struct integer_arithmetic : numeric_arithmetic<TDef>, modulo<TDef> {};
+
+template <typename TDef>
+struct equality_comparible : equals<TDef>, not_equals<TDef> {};
+
+template <typename TDef>
+struct orderable : equality_comparible<TDef>, less_than<TDef>, less_equal<TDef>, greater_than<TDef>, greater_equal<TDef> {};
+
 template <typename Self, typename T>
 class opaque_typedef {
 public:
@@ -116,6 +137,33 @@ public:
     using std::swap;
     swap(lhs._value, rhs._value);
   }
+protected:
+  T const & self() const { return _value; }
 private:
   T _value;
 };
+
+#include <boost/preprocessor/variadic/to_seq.hpp>
+#include <boost/preprocessor/tuple/to_seq.hpp>
+#include <boost/preprocessor/seq/for_each.hpp>
+
+#define OPAQUE_TYPEDEF_CONVERT(r, x, type) operator type() const;
+#define OPAQUE_TYPEDEF_CONVERSIONS(...) \
+    BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_CONVERT, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
+  }
+
+#define CREATE_CONVERTABLE_OPAQUE_TYPEDEF(name, basetype, ...) \
+  struct name : opaque_typedef<name, basetype> \
+  BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_PARENT, name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
+  { \
+    using opaque_typedef::opaque_typedef; \
+    OPAQUE_TYPEDEF_CONVERSIONS
+
+
+#define OPAQUE_TYPEDEF_PARENT(r, name, parent) , parent < name >
+#define CREATE_OPAQUE_TYPEDEF(name, basetype, ...) \
+  struct name : opaque_typedef<name, basetype> \
+  BOOST_PP_SEQ_FOR_EACH(OPAQUE_TYPEDEF_PARENT, name, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
+  { \
+    using opaque_typedef::opaque_typedef; \
+  }

+ 221 - 7
opaque_typedef.xcodeproj/project.pbxproj

@@ -7,13 +7,64 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		CD3C80781D63F2CC00ACC795 /* opaque_typedef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3C80771D63F2CC00ACC795 /* opaque_typedef.cpp */; };
+		CD70490220C48B68007C944C /* opaque_typedef.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CD3C80761D63EE7800ACC795 /* opaque_typedef.hpp */; };
+		CD70490C20C48B75007C944C /* libopaque_typedef.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD3C80681D63EE0000ACC795 /* libopaque_typedef.dylib */; };
+		CD70491220C48B7C007C944C /* opaque_typedef_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD7048F020C48AE4007C944C /* opaque_typedef_test.cpp */; };
+		CD70491520C48B8C007C944C /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD7048FB20C48B30007C944C /* GoogleMock.framework */; };
 /* End PBXBuildFile section */
 
+/* Begin PBXContainerItemProxy section */
+		CD7048FA20C48B30007C944C /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05818F861A685AEA0072A469;
+			remoteInfo = GoogleMock;
+		};
+		CD7048FC20C48B30007C944C /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96ABD1A68600C00204102;
+			remoteInfo = gmock;
+		};
+		CD7048FE20C48B30007C944C /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96B1F1A68634900204102;
+			remoteInfo = gtest;
+		};
+		CD70490020C48B30007C944C /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			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 */;
+			proxyType = 1;
+			remoteGlobalIDString = 05818F851A685AEA0072A469;
+			remoteInfo = GoogleMock;
+		};
+/* 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; };
+		CD3C80681D63EE0000ACC795 /* libopaque_typedef.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libopaque_typedef.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD3C80761D63EE7800ACC795 /* opaque_typedef.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = opaque_typedef.hpp; sourceTree = "<group>"; };
-		CD3C80771D63F2CC00ACC795 /* opaque_typedef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_typedef.cpp; sourceTree = "<group>"; };
+		CD7048F020C48AE4007C944C /* opaque_typedef_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opaque_typedef_test.cpp; 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>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -24,13 +75,25 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		CD70490420C48B75007C944C /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD70491520C48B8C007C944C /* GoogleMock.framework in Frameworks */,
+				CD70490C20C48B75007C944C /* libopaque_typedef.dylib in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
 		CD3C805F1D63EE0000ACC795 = {
 			isa = PBXGroup;
 			children = (
+				CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */,
 				CD3C806A1D63EE0000ACC795 /* src */,
+				CD7048F220C48B19007C944C /* test */,
+				CD70490820C48B75007C944C /* opaque_typedef_test */,
 				CD3C80691D63EE0000ACC795 /* Products */,
 			);
 			sourceTree = "<group>";
@@ -39,6 +102,7 @@
 			isa = PBXGroup;
 			children = (
 				CD3C80681D63EE0000ACC795 /* libopaque_typedef.dylib */,
+				CD70490720C48B75007C944C /* opaque_typedef_test.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -47,11 +111,37 @@
 			isa = PBXGroup;
 			children = (
 				CD3C80761D63EE7800ACC795 /* opaque_typedef.hpp */,
-				CD3C80771D63F2CC00ACC795 /* opaque_typedef.cpp */,
 			);
 			name = src;
 			sourceTree = "<group>";
 		};
+		CD7048F220C48B19007C944C /* test */ = {
+			isa = PBXGroup;
+			children = (
+				CD7048F020C48AE4007C944C /* opaque_typedef_test.cpp */,
+			);
+			name = test;
+			sourceTree = "<group>";
+		};
+		CD7048F420C48B30007C944C /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CD7048FB20C48B30007C944C /* GoogleMock.framework */,
+				CD7048FD20C48B30007C944C /* gmock.framework */,
+				CD7048FF20C48B30007C944C /* gtest.framework */,
+				CD70490120C48B30007C944C /* GoogleMockTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		CD70490820C48B75007C944C /* opaque_typedef_test */ = {
+			isa = PBXGroup;
+			children = (
+				CD70490B20C48B75007C944C /* Info.plist */,
+			);
+			path = opaque_typedef_test;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -59,6 +149,7 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD70490220C48B68007C944C /* opaque_typedef.hpp in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -77,11 +168,30 @@
 			);
 			dependencies = (
 			);
-			name = "opaque_typedef";
-			productName = "opaque_typedef";
+			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" */;
+			buildPhases = (
+				CD70490320C48B75007C944C /* Sources */,
+				CD70490420C48B75007C944C /* Frameworks */,
+				CD70490520C48B75007C944C /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				CD70491420C48B88007C944C /* PBXTargetDependency */,
+				CD70490E20C48B75007C944C /* PBXTargetDependency */,
+			);
+			name = opaque_typedef_test;
+			productName = opaque_typedef_test;
+			productReference = CD70490720C48B75007C944C /* opaque_typedef_test.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -94,6 +204,9 @@
 					CD3C80671D63EE0000ACC795 = {
 						CreatedOnToolsVersion = 7.2.1;
 					};
+					CD70490620C48B75007C944C = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
 				};
 			};
 			buildConfigurationList = CD3C80631D63EE0000ACC795 /* Build configuration list for PBXProject "opaque_typedef" */;
@@ -106,24 +219,92 @@
 			mainGroup = CD3C805F1D63EE0000ACC795;
 			productRefGroup = CD3C80691D63EE0000ACC795 /* Products */;
 			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = CD7048F420C48B30007C944C /* Products */;
+					ProjectRef = CD7048F320C48B30007C944C /* GoogleMock.xcodeproj */;
+				},
+			);
 			projectRoot = "";
 			targets = (
 				CD3C80671D63EE0000ACC795 /* opaque_typedef */,
+				CD70490620C48B75007C944C /* opaque_typedef_test */,
 			);
 		};
 /* End PBXProject section */
 
+/* Begin PBXReferenceProxy section */
+		CD7048FB20C48B30007C944C /* GoogleMock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = GoogleMock.framework;
+			remoteRef = CD7048FA20C48B30007C944C /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD7048FD20C48B30007C944C /* gmock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gmock.framework;
+			remoteRef = CD7048FC20C48B30007C944C /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD7048FF20C48B30007C944C /* gtest.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gtest.framework;
+			remoteRef = CD7048FE20C48B30007C944C /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD70490120C48B30007C944C /* GoogleMockTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = GoogleMockTests.xctest;
+			remoteRef = CD70490020C48B30007C944C /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+		CD70490520C48B75007C944C /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
 		CD3C80641D63EE0000ACC795 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CD3C80781D63F2CC00ACC795 /* opaque_typedef.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		CD70490320C48B75007C944C /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD70491220C48B7C007C944C /* opaque_typedef_test.cpp 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;
+			targetProxy = CD70491320C48B88007C944C /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin XCBuildConfiguration section */
 		CD3C80711D63EE0000ACC795 /* Debug */ = {
 			isa = XCBuildConfiguration;
@@ -161,6 +342,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = /opt/local/include;
 				MACOSX_DEPLOYMENT_TARGET = 10.10;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -198,6 +380,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = /opt/local/include;
 				MACOSX_DEPLOYMENT_TARGET = 10.10;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = macosx;
@@ -230,6 +413,28 @@
 			};
 			name = Release;
 		};
+		CD70491020C48B75007C944C /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = opaque_typedef_test/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.opaque-typedef-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CD70491120C48B75007C944C /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = opaque_typedef_test/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.opaque-typedef-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -249,6 +454,15 @@
 				CD3C80751D63EE0000ACC795 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CD70490F20C48B75007C944C /* Build configuration list for PBXNativeTarget "opaque_typedef_test" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD70491020C48B75007C944C /* Debug */,
+				CD70491120C48B75007C944C /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
 		};
 /* End XCConfigurationList section */
 	};

+ 43 - 0
opaque_typedef_test.cpp

@@ -0,0 +1,43 @@
+//
+//  opaque_typedef_test.cpp
+//  opaque_typedef
+//
+//  Created by Sam Jaffe on 6/3/18.
+//
+
+#include <gmock/gmock.h>
+#include <cmath>
+
+#include "opaque_typedef.hpp"
+
+struct degree;
+struct radian;
+
+CREATE_CONVERTABLE_OPAQUE_TYPEDEF(degree, double, orderable)(radian);
+CREATE_CONVERTABLE_OPAQUE_TYPEDEF(radian, double, orderable)(degree);
+
+radian::operator degree() const {
+  return degree{ self() * 90.0 / M_PI_2 };
+}
+
+degree::operator radian() const {
+  return radian{ self() * M_PI_2 / 90.0 };
+}
+
+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));
+}

+ 24 - 0
opaque_typedef_test/Info.plist

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>