Created
April 17, 2019 21:57
-
-
Save johnmcfarlane/cf8f9892732e84dcd66fde619b072991 to your computer and use it in GitHub Desktop.
number<> & pals
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 <functional> | |
#include <type_traits> | |
#include <utility> | |
namespace cnl2 { | |
//////////////////////////////////////////////////////////////////////////////// | |
// Arithmetic - is a something that supports arithmetic operators | |
// | |
// As well as a fundamental arithmetic type (unsigned short int, __int128, etc.) | |
// a class-based type (a pack<> or a number<> instantiation) although bool is | |
// excluded. | |
template<typename T> | |
struct is_arithmetic : std::is_arithmetic<T> { | |
}; | |
template<> | |
struct is_arithmetic<bool> : std::false_type { | |
}; | |
template<class T> | |
concept bool Arithmetic = is_arithmetic<T>::value; | |
//////////////////////////////////////////////////////////////////////////////// | |
// operators | |
struct operator_base { | |
}; | |
template<class T> | |
concept bool Operator = std::is_base_of_v<operator_base, T>; | |
struct comparison_operator_base { | |
}; | |
template<class T> | |
concept bool ComparisonOperator = std::is_base_of_v<comparison_operator_base, T>; | |
// cast - convert from value of one type to equivalent value of another type | |
struct cast : operator_base { | |
template<Arithmetic Lhs, Arithmetic Rhs> | |
constexpr auto operator()(Rhs const& rhs) | |
{ | |
return static_cast<Lhs>(rhs); | |
} | |
}; | |
// unary arithmetic operators | |
struct minus : operator_base { | |
template<Arithmetic Rhs> | |
constexpr auto operator()(Rhs const& rhs) | |
{ | |
return -rhs; | |
} | |
}; | |
struct plus : operator_base { | |
template<Arithmetic Rhs> | |
constexpr auto operator()(Rhs const& rhs) | |
{ | |
return +rhs; | |
} | |
}; | |
// binary arithmetic operators | |
struct add : operator_base { | |
template<Arithmetic Lhs, Arithmetic Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return lhs+rhs; | |
} | |
}; | |
// binary comparison operators | |
struct equal : operator_base, comparison_operator_base { | |
template<Arithmetic Lhs, Arithmetic Rhs> | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return lhs==rhs; | |
} | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// primary operation template - operator customisation points | |
template<typename Op, Arithmetic ... Args> | |
struct operation; | |
//////////////////////////////////////////////////////////////////////////////// | |
// operation template specializations - operator customisation points | |
// default cast operation | |
template<Arithmetic Lhs, Arithmetic Rhs> | |
struct operation<cast, Lhs, Rhs> { | |
constexpr auto operator()(Rhs const& rhs) const | |
{ | |
return cast{}.template operator()<Lhs>(rhs); | |
} | |
}; | |
// default unary operation | |
template<Operator Op, Arithmetic Rhs> | |
struct operation<Op, Rhs> { | |
constexpr auto operator()(Rhs const& rhs) const | |
{ | |
return Op{}(rhs); | |
} | |
}; | |
// default binary operation | |
template<Operator Op, Arithmetic Lhs, Arithmetic Rhs> | |
struct operation<Op, Lhs, Rhs> { | |
constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
{ | |
return Op{}(lhs, rhs); | |
} | |
}; | |
// // default equal operation | |
// template<Arithmetic Lhs, Arithmetic Rhs> | |
// struct operation<equal, Lhs, Rhs> { | |
// constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
// { | |
// return lhs==rhs; | |
// } | |
// }; | |
//////////////////////////////////////////////////////////////////////////////// | |
// operate and convert - type-deducing helpers for operation | |
template<Operator Op, Arithmetic Lhs, Arithmetic Rhs> | |
constexpr auto convert(Rhs const& rhs) | |
{ | |
return operation<cast, Lhs, Rhs>{}(rhs); | |
} | |
template<typename Op, Arithmetic Rhs> | |
constexpr auto operate(Rhs const& rhs) | |
{ | |
return operation<Op, Rhs>{}(rhs); | |
} | |
template<typename Op, Arithmetic LhsRep, Arithmetic RhsRep> | |
constexpr auto operate(LhsRep const& lhs, RhsRep const& rhs) | |
{ | |
return operation<Op, LhsRep, RhsRep>{}(lhs, rhs); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// tags | |
struct tag_base { | |
}; | |
template<typename Tag> | |
concept bool Tagging = std::is_base_of_v<tag_base, Tag>; | |
struct default_tag : tag_base { | |
}; | |
template<Tagging Tag> | |
constexpr auto operator+(Tag, Tag) { return Tag{}; } | |
template<Tagging Tag> | |
constexpr auto operator==(Tag, Tag) { return Tag{}; } | |
//////////////////////////////////////////////////////////////////////////////// | |
// number - wrapper brings together storage and behavior | |
template<Tagging Tag = default_tag, Arithmetic Rep = int> | |
class number; | |
template<typename T> | |
struct is_number : std::false_type { | |
}; | |
template<Tagging Tag, Arithmetic Rep> | |
struct is_number<number<Tag, Rep>> : std::true_type { | |
}; | |
template<Tagging Tag, Arithmetic Rep> | |
struct is_arithmetic<number<Tag, Rep>> : std::true_type { | |
}; | |
template<typename T> | |
concept bool Number = is_number<T>::value; | |
template<Tagging Tag, Arithmetic Rep> | |
class number { | |
public: | |
explicit constexpr number(Rep const& r, int) : r_(r) {} | |
public: | |
using rep = Rep; | |
using tag = Tag; | |
// converting constructor | |
template<Arithmetic Rhs> | |
explicit constexpr number(Rhs const& rhs) | |
: r_(convert<cast, Rhs>(rhs)) { } | |
template<Arithmetic Lhs> | |
constexpr operator Lhs() const | |
{ | |
return convert<cast, Tag, Lhs>(r_); | |
} | |
// private: | |
// template<class RhsTag, Arithmetic RhsRep> | |
// friend constexpr auto operator+(number const& lhs, number<RhsTag, RhsRep> const& rhs) | |
// { | |
// return number(operate<add, Tag, RhsTag>(lhs.r_, rhs.r_), int{}); | |
// } | |
// | |
// template<class RhsTag, Arithmetic RhsRep> | |
// friend constexpr auto operator==(number const& lhs, number<RhsTag, RhsRep> const& rhs) | |
// { | |
// return number(operate<equal, Tag, RhsTag>(lhs.r_, rhs.r_), int{}); | |
// } | |
// template<Arithmetic Rhs> | |
// friend constexpr auto operator+(number const& lhs, Rhs const& rhs) | |
// { | |
// return operate<add>(lhs, rhs); | |
// } | |
// | |
// template<Arithmetic Lhs> | |
// friend constexpr auto operator+(Lhs const& lhs, number const& rhs) | |
// { | |
// return number(operate<add>(lhs, rhs), int{}); | |
// } | |
template<Arithmetic Lhs, Arithmetic Rhs> | |
friend constexpr auto operator==(Lhs const& lhs, Rhs const& rhs) | |
{ | |
return operate<equal>(lhs, rhs); | |
} | |
rep r_; | |
}; | |
// wrap helper function | |
template<typename Tag, typename Rep> | |
constexpr auto wrap(Rep const& r) { | |
return number<Tag, Rep>(r, int{}); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// number-specific operate | |
// binary arithmetic | |
template<Operator Op, Tagging Tag, Arithmetic LhsRep, Arithmetic RhsRep> | |
constexpr auto operate(number<Tag, LhsRep> const& lhs, number<Tag, RhsRep> const& rhs) | |
{ | |
return wrap<Tag>(operate<Op>(lhs.r_, rhs.r_)); | |
} | |
template<Operator Op, Tagging LhsTag, Arithmetic LhsRep, Arithmetic Rep> | |
constexpr auto operate(number<LhsTag, LhsRep> const& lhs, Rep const& rhs) | |
{ | |
return wrap<LhsTag>(operate<Op>(lhs.r_, rhs)); | |
} | |
template<Operator Op, Arithmetic Rep, Tagging RhsTag, Arithmetic RhsRep> | |
constexpr auto operate(Rep const& lhs, number<RhsTag, RhsRep> const& rhs) | |
{ | |
return wrap<RhsTag>(operate<Op>(lhs, rhs.r_)); | |
} | |
// comparison | |
template<ComparisonOperator Op, Tagging Tag, Arithmetic LhsRep, Arithmetic RhsRep> | |
constexpr auto operate(number<Tag, LhsRep> const& lhs, number<Tag, RhsRep> const& rhs) | |
{ | |
return operate<Op>(lhs.r_, rhs.r_); | |
} | |
template<ComparisonOperator Op, Tagging LhsTag, Arithmetic LhsRep, Arithmetic Rep> | |
constexpr auto operate(number<LhsTag, LhsRep> const& lhs, Rep const& rhs) | |
requires !Number<Rep> | |
{ | |
return operate<Op>(lhs.r_, rhs); | |
} | |
template<ComparisonOperator Op, Arithmetic Rep, Tagging RhsTag, Arithmetic RhsRep> | |
constexpr auto operate(Rep const& lhs, number<RhsTag, RhsRep> const& rhs) | |
requires !Number<Rep> | |
{ | |
return operate<Op>(lhs, rhs.r_); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// number-specific operation | |
// // default binary operation | |
// template<Operator Op, Number Lhs, Number Rhs> | |
// struct operation<Op, Lhs, Rhs> { | |
// constexpr auto operator()(Lhs const& lhs, Rhs const& rhs) const | |
// { | |
// return Op{}(lhs, rhs); | |
// } | |
// }; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
template<typename Lhs, typename Rhs> | |
constexpr bool identical(Lhs const& lhs, Rhs const& rhs) | |
{ | |
static_assert(std::is_same_v<Lhs, Rhs>); | |
return lhs==rhs; | |
} | |
namespace test_comparison { | |
static_assert(identical(42, 42)); | |
static_assert(identical(cnl2::number<>{42}, cnl2::number<>{42})); | |
// static_assert(identical(cnl2::number<>{42}, static_cast<cnl2::number<>>(42))); | |
// static_assert(identical(cnl2::number<>{42}, 42)); | |
// static_assert(identical(42, cnl2::number<>{42}); | |
} | |
//namespace test_cast { | |
// static_assert(identical(cnl2::number<>{8}, cnl2::number<>{5}+cnl2::number<>{3}); | |
//} | |
// | |
////static_assert(identical(cnl2::number<>{8}, 5+cnl2::number<>{3}); | |
////static_assert(identical(cnl2::number<>{8}, cnl2::number<>{5}+3); | |
////static_assert(identical(cnl2::number<>{15}, cnl2::number<>{5}*cnl2::number<>{3}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment