Last active
May 17, 2017 02:45
-
-
Save willkill07/dcd6de574130d2f2a0fe7831b7e1a500 to your computer and use it in GitHub Desktop.
Strong Typing in C++14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <type_traits> | |
| #include <utility> | |
| namespace detail { | |
| template <typename Seq, std::size_t Idx, typename T, T Val, T DefaultVal> | |
| struct make_list_with_index_value; | |
| template <std::size_t Idx, typename T, T Val, T DefaultVal, std::size_t ... Ids> | |
| struct make_list_with_index_value<std::index_sequence<Ids...>, Idx, T, Val, DefaultVal> { | |
| using type = std::integer_sequence<T, ((Idx == Ids) ? Val : DefaultVal)...>; | |
| }; | |
| } | |
| template <std::size_t Size, std::size_t Index, typename T, T Value, T DefaultValue> | |
| using make_list_with_index_value = typename detail::make_list_with_index_value<std::make_index_sequence<Size>, Index, T, Value, DefaultValue>::type; | |
| namespace ops { | |
| template <typename T1, typename T2> struct plus { | |
| static_assert(std::is_same<T1,T2>::value, "mismatched units encountered"); | |
| using type = T1; | |
| }; | |
| template <typename T1, typename T2> using minus = plus<T1,T2>; | |
| template <typename, typename> struct multiplies; | |
| template <typename, typename> struct divides; | |
| template <typename, typename> struct pow; | |
| template <int... T1, int... T2> | |
| struct multiplies<std::integer_sequence<int, T1...>, std::integer_sequence<int, T2...>> { | |
| using type = std::integer_sequence<int, (T1 + T2)...>; | |
| }; | |
| template <int... T1, int... T2> | |
| struct divides<std::integer_sequence<int, T1...>, std::integer_sequence<int, T2...>> { | |
| using type = std::integer_sequence<int, (T1 - T2)...>; | |
| }; | |
| template <int... T1, int Value> | |
| struct pow<std::integer_sequence<int, T1...>, std::integer_sequence<int, Value>> { | |
| using type = std::integer_sequence<int, (T1 * Value)...>; | |
| }; | |
| } | |
| namespace units { | |
| constexpr unsigned int Dims = 7; | |
| using none = make_list_with_index_value<Dims, 0, int, 0, 0>; | |
| using length = make_list_with_index_value<Dims, 0, int, 1, 0>; | |
| using mass = make_list_with_index_value<Dims, 1, int, 1, 0>; | |
| using time = make_list_with_index_value<Dims, 2, int, 1, 0>; | |
| using current = make_list_with_index_value<Dims, 3, int, 1, 0>; | |
| using temperature = make_list_with_index_value<Dims, 4, int, 1, 0>; | |
| using substance = make_list_with_index_value<Dims, 5, int, 1, 0>; | |
| using luminous = make_list_with_index_value<Dims, 6, int, 1, 0>; | |
| } | |
| template <typename T, typename Tag> | |
| struct StrongType { | |
| private: | |
| T value; | |
| public: | |
| using type = T; | |
| explicit StrongType(T v) : value(v) {} | |
| StrongType() = default; | |
| StrongType(StrongType const &) = default; | |
| StrongType(StrongType &&) = default; | |
| explicit operator T() const { | |
| return value; | |
| }; | |
| StrongType& operator+=(const StrongType &o) { | |
| value += o.value; | |
| return *this; | |
| } | |
| StrongType& operator-=(const StrongType &o) { | |
| value -= o.value; | |
| return *this; | |
| } | |
| template <typename U, typename = typename std::enable_if<std::is_arithmetic<U>::type>> | |
| StrongType& operator*=(U v) { | |
| value += v; | |
| return *this; | |
| } | |
| template <typename U, typename = typename std::enable_if<std::is_arithmetic<U>::type>> | |
| StrongType& operator/=(U v) { | |
| value /= v; | |
| return *this; | |
| } | |
| StrongType operator-() const { | |
| return StrongType{-value}; | |
| } | |
| StrongType operator+() const { | |
| return StrongType{value}; | |
| } | |
| #define GEN_OP(BINOP, FN_TAG) \ | |
| template < \ | |
| typename U, typename UTag, \ | |
| typename Ret = StrongType<decltype(T() BINOP U()), typename FN_TAG <Tag,UTag>::type>> \ | |
| Ret operator BINOP(const StrongType<U, UTag> & o) const { \ | |
| return Ret{value BINOP static_cast<U>(o)}; \ | |
| } | |
| GEN_OP(+,ops::plus) | |
| GEN_OP(-,ops::minus) | |
| GEN_OP(*,ops::multiplies) | |
| GEN_OP(/,ops::divides) | |
| #undef GEN_OP | |
| }; | |
| #define GEN_OP(BINOP, FN_TAG) \ | |
| template <typename T, typename Tag, typename U, \ | |
| typename Ret = StrongType<decltype(T() BINOP U()), typename FN_TAG <Tag, units::none>::type>, \ | |
| typename = typename std::enable_if<std::is_arithmetic<U>::value>::type> \ | |
| Ret operator BINOP(StrongType<T,Tag> const & v1, U const v2) { \ | |
| return Ret{static_cast<T>(v1) BINOP v2}; \ | |
| } \ | |
| \ | |
| template <typename T, typename Tag, typename U, \ | |
| typename Ret = StrongType<decltype(U() BINOP T()), typename FN_TAG <units::none, Tag>::type>, \ | |
| typename = typename std::enable_if<std::is_arithmetic<U>::value>::type> \ | |
| Ret operator BINOP(U const v1, StrongType<T,Tag> const & v2) { \ | |
| return Ret{v1 BINOP static_cast<T>(v2)}; \ | |
| } | |
| GEN_OP(*,ops::multiplies) | |
| GEN_OP(/,ops::divides) | |
| #undef GEN_OP |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment