Browse Source

Make conversions no longer quite so awful by providing an internal-only cast function 'as<T>()'.

This does have the unfortunate side-effect of causing us to produce linker errors instead of compiler errors when trying to perform incompatible conversions, however.
Sam Jaffe 5 years ago
parent
commit
6556b185e1

+ 0 - 16
include/opaque_typedef/convertable.hpp

@@ -1,16 +0,0 @@
-#pragma once
-
-namespace types {
-  
-  template <typename To>
-  struct Convertable {
-    template <typename Self>
-    class type {
-    private:
-      Self const & self() const { return *(Self const *)(void const *)(this); }
-    public:
-      operator To() const;
-    };
-  };
-  
-}

+ 18 - 4
include/opaque_typedef/opaque_typedef.hpp

@@ -9,15 +9,25 @@
 
 #pragma once
 
+#include <type_traits>
+
 #include "comparable.hpp"
-#include "convertable.hpp"
 
 namespace types {
+  template <typename Base, typename Tag, template <typename> class... Skills>
+  class opaque_typedef;
+  
+  template <typename T> struct is_opaque_typedef {
+    constexpr static bool const value = false;
+  };
+  
+  template <typename B, typename T, template <typename> class... S>
+  struct is_opaque_typedef<opaque_typedef<B, T, S...>> {
+    constexpr static bool const value = true;
+  };
+
   template <typename Base, typename Tag, template <typename> class... Skills>
   class opaque_typedef : public Skills<opaque_typedef<Base, Tag, Skills...>>... {
-  public:
-    using super = opaque_typedef;
-    
   private:
     Base value_;
     
@@ -25,8 +35,12 @@ namespace types {
     opaque_typedef() = default;
     explicit opaque_typedef(Base const & value) : value_(value) {};
     explicit opaque_typedef(Base && value) : value_(std::forward<Base>(value)) {};
+    template <typename B, typename T, template <typename> class... S>
+    opaque_typedef(opaque_typedef<B, T, S...> const & other)
+        : opaque_typedef(other.template as<opaque_typedef>()) {}
     
     Base const & get() const { return value_; }
     explicit operator Base const &() const { return value_; }
+    template <typename To> To as() const;
   };
 }

+ 6 - 15
test/opaque_typedef_test.cpp

@@ -10,24 +10,15 @@
 
 #include "opaque_typedef/opaque_typedef.hpp"
 
-struct degree;
-struct radian;
+using degree = types::opaque_typedef<double, struct degree_tag, types::Comparable>;
+using radian = types::opaque_typedef<double, struct radian_tag, types::Comparable>;
 
-struct degree : types::opaque_typedef<double, struct degree_tag, types::Comparable, types::Convertable<radian>::type> {
-  using super::super;
-};
-struct radian : types::opaque_typedef<double, struct radian_tag, types::Comparable, types::Convertable<degree>::type> {
-  using super::super;
-};
-
-template<> template <>
-types::Convertable<degree>::type<radian::super>::operator degree() const {
-  return degree{ self().get() * 90.0 / M_PI_2 };
+template <> template <> radian degree::as() const {
+  return radian{ get() * M_PI_2 / 90.0 };
 }
 
-template <> template <>
-types::Convertable<radian>::type<degree::super>::operator radian() const {
-  return radian{ self().get() * M_PI_2 / 90.0 };
+template <> template <> degree radian::as() const {
+  return degree{ get() * 90.0 / M_PI_2 };
 }
 
 void PrintTo(radian const & ot, std::ostream * os) {