Skip to content

Instantly share code, notes, and snippets.

@johnmcfarlane
Created April 17, 2019 21:57
Show Gist options
  • Save johnmcfarlane/cf8f9892732e84dcd66fde619b072991 to your computer and use it in GitHub Desktop.
Save johnmcfarlane/cf8f9892732e84dcd66fde619b072991 to your computer and use it in GitHub Desktop.
number<> & pals
#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