瀏覽代碼

Initial commit of variant, a type-safe replacement for tagged-unions

Samuel Jaffe 9 年之前
當前提交
e70d619d07

+ 54 - 0
variant.cpp

@@ -0,0 +1,54 @@
+//
+//  vairant.cpp
+//  variant
+//
+//  Created by Sam Jaffe on 1/30/16.
+//  Copyright © 2016 Sam Jaffe. All rights reserved.
+//
+
+#include "variant.hpp"
+#include <iostream>
+
+struct test{
+    int * holder;
+    test() {
+        std::cout << "test()" << std::endl;
+        holder = new int();
+    }
+    
+    test(test&& old) : holder(nullptr) {
+        std::cout << "test(test&&)" << std::endl;
+        std::swap(holder,old.holder);
+    }
+    test(const test& old) {
+        std::cout << "test(const test&)" << std::endl;
+        holder = new int(*old.holder);
+    }
+    ~test()
+    {
+        std::cout << "~test()" << std::endl;
+        delete holder;
+    }
+};
+
+
+int main() {
+    using my_var = variant<std::string, test>;
+    
+    my_var d;
+    
+    d.set<std::string>("First string");
+    std::cout << d.get<std::string>() << std::endl;
+    
+    d.set<test>();
+    *d.get<test>().holder = 42;
+    
+    my_var e(std::move(d));
+    std::cout << *e.get<test>().holder << std::endl;
+    
+    *e.get<test>().holder = 43;
+    
+    d = e;
+    
+    std::cout << *d.get<test>().holder << std::endl;    
+}

+ 162 - 0
variant.hpp

@@ -0,0 +1,162 @@
+//
+//  variant.hpp
+//  variant
+//
+//  Created by Sam Jaffe on 1/30/16.
+//  Copyright © 2016 Sam Jaffe. All rights reserved.
+//
+
+#ifndef variant_h
+#define variant_h
+#pragma once
+
+#include <cstdlib>
+#include <utility>
+#include <memory>
+
+// Max of a list of values
+template <size_t T, size_t... Ts> struct static_max;
+
+template <size_t T>
+struct static_max<T> {
+  static const constexpr size_t value = T;
+};
+
+template <size_t T1, size_t T2, size_t... Ts>
+struct static_max<T1, T2, Ts...> {
+  static const constexpr size_t value = T1 > T2 ? static_max<T1, Ts...>::value : static_max<T2, Ts...>::value;
+};
+
+// Type index in a list
+template <typename F, typename... Ts> struct type_index;
+
+template <typename F>
+struct type_index<F> {};
+
+template <typename F, typename T, typename... Ts>
+struct type_index<F, T, Ts...> {
+  static const constexpr size_t value = type_index<F, Ts...>::value;
+};
+
+template <typename F, typename... Ts>
+struct type_index<F, F, Ts...> {
+  static const constexpr size_t value = sizeof...(Ts);
+};
+
+template <typename... Ts> struct variant_helper;
+
+template<typename F, typename... Ts>
+struct variant_helper<F, Ts...> {
+  inline static void destroy(size_t id, void * data)
+  {
+    if (id == sizeof...(Ts))
+      reinterpret_cast<F*>(data)->~F();
+    else
+      variant_helper<Ts...>::destroy(id, data);
+  }
+  
+  inline static void move(size_t old_t, void * old_v, void * new_v)
+  {
+    if (old_t == sizeof...(Ts))
+      new (new_v) F(std::move(*reinterpret_cast<F*>(old_v)));
+    else
+      variant_helper<Ts...>::move(old_t, old_v, new_v);
+  }
+  
+  inline static void copy(size_t old_t, const void * old_v, void * new_v)
+  {
+    if (old_t == sizeof...(Ts))
+      new (new_v) F(*reinterpret_cast<const F*>(old_v));
+    else
+      variant_helper<Ts...>::copy(old_t, old_v, new_v);
+  }   
+};
+
+template<> struct variant_helper<>  {
+  inline static void destroy(size_t id, void * data) { }
+  inline static void move(size_t old_t, void * old_v, void * new_v) { }
+  inline static void copy(size_t old_t, const void * old_v, void * new_v) { }
+};
+
+template <typename... Ts>
+struct variant {
+private:
+  static const constexpr size_t data_size = static_max<sizeof(Ts)...>::value;
+  static const constexpr size_t data_align = static_max<alignof(Ts)...>::value;
+  
+  using data_t = typename std::aligned_storage<data_size, data_align>::type;
+  
+  using helper_t = variant_helper<Ts...>;
+  
+  static inline size_t invalid_type() {
+    return 0xFFFFFFFF;
+  }
+  
+  size_t type_id;
+  data_t data;
+public:
+  variant() : type_id(invalid_type()) {   }
+  
+  variant(const variant<Ts...>& old) : type_id(old.type_id)
+  {
+    helper_t::copy(old.type_id, &old.data, &data);
+  }
+  
+  variant(variant<Ts...>&& old) : type_id(old.type_id)
+  {
+    helper_t::move(old.type_id, &old.data, &data);
+  }
+  
+  // Serves as both the move and the copy asignment operator.
+  variant<Ts...>& operator= (variant<Ts...> old)
+  {
+    std::swap(type_id, old.type_id);
+    std::swap(data, old.data);
+    
+    return *this;
+  }
+  
+  template<typename T>
+  bool is() const {
+    return (type_id == type_index<T, Ts...>::value);
+  }
+  
+  bool valid() const {
+    return (type_id != invalid_type());
+  }
+  
+  template<typename T, typename... Args>
+  void set(Args&&... args)
+  {
+    // First we destroy the current contents
+    helper_t::destroy(type_id, &data);
+    new (&data) T(std::forward<Args>(args)...);
+    type_id = type_index<T, Ts...>::value;
+  }
+  
+  template<typename T>
+  T& get()
+  {
+    // It is a dynamic_cast-like behaviour
+    if (type_id == type_index<T, Ts...>::value)
+      return *reinterpret_cast<T*>(&data);
+    else
+      throw std::bad_cast();
+  }
+  
+  template<typename T>
+  T const& get() const
+  {
+    // It is a dynamic_cast-like behaviour
+    if (type_id == type_index<T, Ts...>::value)
+      return *reinterpret_cast<T const*>(&data);
+    else
+      throw std::bad_cast();
+  }
+  
+  ~variant() {
+    helper_t::destroy(type_id, &data);
+  }
+};
+
+#endif /* variant_h */

+ 250 - 0
variant.xcodeproj/project.pbxproj

@@ -0,0 +1,250 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		CDB2F7251C5D43010067C2EC /* variant.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7241C5D43000067C2EC /* variant.hpp */; };
+		CDB2F7281C5D45B60067C2EC /* variant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7261C5D45B60067C2EC /* variant.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		CDB2F7161C5D42EB0067C2EC /* libvariant.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libvariant.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDB2F7241C5D43000067C2EC /* variant.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = variant.hpp; sourceTree = "<group>"; tabWidth = 2; };
+		CDB2F7261C5D45B60067C2EC /* variant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = variant.cpp; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		CDB2F7131C5D42EB0067C2EC /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		CDB2F70D1C5D42EB0067C2EC = {
+			isa = PBXGroup;
+			children = (
+				CDB2F7241C5D43000067C2EC /* variant.hpp */,
+				CDB2F7261C5D45B60067C2EC /* variant.cpp */,
+				CDB2F7171C5D42EB0067C2EC /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		CDB2F7171C5D42EB0067C2EC /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CDB2F7161C5D42EB0067C2EC /* libvariant.dylib */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		CDB2F7141C5D42EB0067C2EC /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDB2F7251C5D43010067C2EC /* variant.hpp in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		CDB2F7151C5D42EB0067C2EC /* variant */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CDB2F7211C5D42EB0067C2EC /* Build configuration list for PBXNativeTarget "variant" */;
+			buildPhases = (
+				CDB2F7121C5D42EB0067C2EC /* Sources */,
+				CDB2F7131C5D42EB0067C2EC /* Frameworks */,
+				CDB2F7141C5D42EB0067C2EC /* Headers */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = variant;
+			productName = variant;
+			productReference = CDB2F7161C5D42EB0067C2EC /* libvariant.dylib */;
+			productType = "com.apple.product-type.library.dynamic";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		CDB2F70E1C5D42EB0067C2EC /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0720;
+				ORGANIZATIONNAME = "Sam Jaffe";
+				TargetAttributes = {
+					CDB2F7151C5D42EB0067C2EC = {
+						CreatedOnToolsVersion = 7.2;
+					};
+				};
+			};
+			buildConfigurationList = CDB2F7111C5D42EB0067C2EC /* Build configuration list for PBXProject "variant" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = CDB2F70D1C5D42EB0067C2EC;
+			productRefGroup = CDB2F7171C5D42EB0067C2EC /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				CDB2F7151C5D42EB0067C2EC /* variant */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+		CDB2F7121C5D42EB0067C2EC /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDB2F7281C5D45B60067C2EC /* variant.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		CDB2F71F1C5D42EB0067C2EC /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		CDB2F7201C5D42EB0067C2EC /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		CDB2F7221C5D42EB0067C2EC /* 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;
+		};
+		CDB2F7231C5D42EB0067C2EC /* 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;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		CDB2F7111C5D42EB0067C2EC /* Build configuration list for PBXProject "variant" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDB2F71F1C5D42EB0067C2EC /* Debug */,
+				CDB2F7201C5D42EB0067C2EC /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CDB2F7211C5D42EB0067C2EC /* Build configuration list for PBXNativeTarget "variant" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDB2F7221C5D42EB0067C2EC /* Debug */,
+				CDB2F7231C5D42EB0067C2EC /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = CDB2F70E1C5D42EB0067C2EC /* Project object */;
+}

+ 80 - 0
variant.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/variant.xcscheme

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CDB2F7151C5D42EB0067C2EC"
+               BuildableName = "libvariant.dylib"
+               BlueprintName = "variant"
+               ReferencedContainer = "container:variant.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDB2F7151C5D42EB0067C2EC"
+            BuildableName = "libvariant.dylib"
+            BlueprintName = "variant"
+            ReferencedContainer = "container:variant.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDB2F7151C5D42EB0067C2EC"
+            BuildableName = "libvariant.dylib"
+            BlueprintName = "variant"
+            ReferencedContainer = "container:variant.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 22 - 0
variant.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -0,0 +1,22 @@
+<?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>SchemeUserState</key>
+	<dict>
+		<key>variant.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>13</integer>
+		</dict>
+	</dict>
+	<key>SuppressBuildableAutocreation</key>
+	<dict>
+		<key>CDB2F7151C5D42EB0067C2EC</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+	</dict>
+</dict>
+</plist>