Last active
January 21, 2016 09:01
-
-
Save Porges/8a24d8fff9e5e6c01038 to your computer and use it in GitHub Desktop.
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 <ratio> | |
#include <type_traits> | |
namespace units { | |
template<class T, int V, class Scale> | |
struct unit | |
{ | |
static_assert(V != 0, "uint with exponent of 0 must be replaced by void"); | |
using type = T; | |
using scale = Scale; | |
static constexpr int value = V; | |
}; | |
namespace details { | |
template<class T> | |
struct type_is { using type = T; }; | |
template<class Left, class Right> | |
struct mul_unit : public type_is< | |
typename std::conditional<Left::value + Right::value == 0, | |
void, // see above | |
unit<typename Left::type, Left::value + Right::value, typename Left::scale>>::type> | |
{ | |
static_assert(std::is_same<typename Left::type, typename Right::type>::value, "Cannot multiply these types"); | |
static_assert(std::is_same<typename Left::scale, typename Right::scale>::value, "Mismatching scales"); | |
}; | |
template<class Right> | |
struct mul_unit<void, Right> : public type_is<Right> {}; | |
template<class Left> | |
struct mul_unit<Left, void> : public type_is<Left> {}; | |
template<> | |
struct mul_unit<void, void> : public type_is<void> {}; | |
template<class Unit> struct inv_unit : public | |
type_is<unit<typename Unit::type, -Unit::value, typename Unit::scale>> {}; | |
template<> struct inv_unit<void> : public type_is<void> {}; | |
template<class Left, class Right> struct div_unit : public | |
type_is<typename mul_unit<Left, typename inv_unit<Right>::type>::type> {}; | |
} | |
template<class L, class R> using mul_unit_t = typename details::mul_unit<L, R>::type; | |
template<class L, class R> using div_unit_t = typename details::div_unit<L, R>::type; | |
template<class From, class To, class Base> | |
struct conversion | |
{ | |
static_assert(std::is_same<From, To>::value, "Invalid conversion - unit mismatch"); | |
static constexpr Base convert(Base b) noexcept | |
{ | |
return b; | |
} | |
}; | |
template<class F, class G, int X, int Y, class SX, class SY, class Base> | |
struct conversion<unit<F, X, SX>, unit<G, Y, SY>, Base> | |
{ | |
static_assert(std::is_same<F, G>::value, "Invalid conversion - unit mismatch"); | |
static_assert(X == Y, "Invalid conversion - dimension mismatch"); | |
static constexpr Base convert(Base b) noexcept | |
{ | |
using div = std::ratio_divide<SX, SY>; | |
return b * div::num / div::den; | |
} | |
}; | |
template<class Base, class Count, class Length, class Mass, class Time> | |
class dim | |
{ | |
Base value_; | |
using this_t = dim<Base, Count, Length, Mass, Time>; | |
public: | |
explicit constexpr dim(Base value) noexcept | |
: value_{value} | |
{} | |
template<class CountR, class LengthR, class MassR, class TimeR> | |
constexpr dim(const dim<Base, CountR, LengthR, MassR, TimeR>& other) noexcept | |
: value_{ | |
// this is probably wrong... | |
conversion<CountR, Count, Base>::convert( | |
conversion<LengthR, Length, Base>::convert( | |
conversion<MassR, Mass, Base>::convert( | |
conversion<TimeR, Time, Base>::convert(other.value())))) | |
} | |
{ | |
} | |
constexpr Base value() const noexcept | |
{ | |
return value_; | |
} | |
#if 0 // need a C++17 compiler | |
constexpr this_t& operator*=(Base right) noexcept | |
{ | |
value_ *= right; | |
return *this; | |
} | |
constexpr this_t& operator/=(Base right) noexcept | |
{ | |
value_ /= right; | |
return *this; | |
} | |
#endif | |
constexpr bool operator==(const this_t& other) const noexcept | |
{ | |
return value_ == other.value_; | |
} | |
constexpr bool operator!=(const this_t& other) const noexcept | |
{ | |
return value_ != other.value_; | |
} | |
}; | |
template<class Base, class Count, class Length, class Mass, class Time> | |
constexpr decltype(auto) operator+( | |
dim<Base, Count, Length, Mass, Time> left, | |
dim<Base, Count, Length, Mass, Time> right) | |
{ | |
return dim<Base, Count, Length, Mass, Time>(left.value() + right.value()); | |
} | |
template<class Base, class Count, class Length, class Mass, class Time> | |
constexpr decltype(auto) operator-( | |
dim<Base, Count, Length, Mass, Time> left, | |
dim<Base, Count, Length, Mass, Time> right) | |
{ | |
return dim<Base, Count, Length, Mass, Time>(left.value() - right.value()); | |
} | |
template<class Base, | |
class CountL, class LengthL, class MassL, class TimeL, | |
class CountR, class LengthR, class MassR, class TimeR> | |
constexpr decltype(auto) operator*( | |
dim<Base, CountL, LengthL, MassL, TimeL> left, | |
dim<Base, CountR, LengthR, MassR, TimeR> right) | |
{ | |
return dim<Base, | |
mul_unit_t<CountL, CountR>, | |
mul_unit_t<LengthL, LengthR>, | |
mul_unit_t<MassL, MassR>, | |
mul_unit_t<TimeL, TimeR>> | |
(left.value() * right.value()); | |
} | |
template<class Base, | |
class CountR, class LengthR, class MassR, class TimeR> | |
constexpr decltype(auto) operator*( | |
Base left, | |
dim<Base, CountR, LengthR, MassR, TimeR> right) | |
{ | |
return dim<Base, | |
CountR, | |
LengthR, | |
MassR, | |
TimeR> | |
(left * right.value()); | |
} | |
template<class Base, | |
class CountL, class LengthL, class MassL, class TimeL> | |
constexpr decltype(auto) operator*( | |
dim<Base, CountL, LengthL, MassL, TimeL> left, | |
Base right) | |
{ | |
return dim<Base, | |
CountL, | |
LengthL, | |
MassL, | |
TimeL> | |
(left.value() * right); | |
} | |
template<class Base, | |
class CountL, class LengthL, class MassL, class TimeL, | |
class CountR, class LengthR, class MassR, class TimeR> | |
constexpr decltype(auto) operator/( | |
dim<Base, CountL, LengthL, MassL, TimeL> left, | |
dim<Base, CountR, LengthR, MassR, TimeR> right) | |
{ | |
return dim<Base, | |
div_unit_t<CountL, CountR>, | |
div_unit_t<LengthL, LengthR>, | |
div_unit_t<MassL, MassR>, | |
div_unit_t<TimeL, TimeR>> | |
(left.value() / right.value()); | |
} | |
template<class T, class Base = size_t> | |
using count = dim<Base, unit<T, 1, std::ratio<1, 1>>, void, void, void>; | |
template<class T> | |
decltype(auto) card(const T& container) noexcept | |
{ | |
return count<typename T::value_type, typename T::size_type>(container.size()); | |
} | |
template<class T, class scale=std::ratio<1, 1>> using define_count = dim<double, unit<T, 1, scale>, void, void, void>; | |
template<class T, class scale=std::ratio<1, 1>> using define_length = dim<double, void, unit<T, 1, scale>, void, void>; | |
template<class T, class scale=std::ratio<1, 1>> using define_mass = dim<double, void, void, unit<T, 1, scale>, void>; | |
template<class T, class scale=std::ratio<1, 1>> using define_time = dim<double, void, void, void, unit<T, 1, scale>>; | |
inline namespace common | |
{ | |
class Degree; | |
class Radian; | |
using degrees = define_count<Degree>; | |
using radians = define_count<Radian>; | |
constexpr degrees operator"" _degrees(long double d) | |
{ | |
return degrees(d); | |
} | |
constexpr radians operator"" _radians(long double d) | |
{ | |
return radians(d); | |
} | |
} | |
constexpr double pi = 3.14159265358979323846; | |
template<> | |
struct conversion<unit<Degree, 1, std::ratio<1, 1>>, unit<Radian, 1, std::ratio<1, 1>>, double> | |
{ | |
static constexpr double convert(double degrees) noexcept | |
{ | |
return degrees * pi / 180; | |
} | |
}; | |
template<> | |
struct conversion<unit<Radian, 1, std::ratio<1, 1>>, unit<Degree, 1, std::ratio<1, 1>>, double> | |
{ | |
static constexpr double convert(double radians) noexcept | |
{ | |
return radians * 180 / pi; | |
} | |
}; | |
namespace si | |
{ | |
using namespace units; | |
class SI; | |
using nanometres = define_length<SI, std::nano>; | |
using micrometres = define_length<SI, std::micro>; | |
using millimetres = define_length<SI, std::milli>; | |
using centimetres = define_length<SI, std::centi>; | |
using metres = define_length<SI>; | |
using kilometres = define_length<SI, std::kilo>; | |
using micrograms = define_mass<SI, std::micro>; | |
using milligrams = define_mass<SI, std::milli>; | |
using grams = define_mass<SI>; | |
using kilograms = define_mass<SI, std::kilo>; | |
using microsecond = define_time<SI, std::micro>; | |
using millisecond = define_time<SI, std::milli>; | |
using seconds = define_time<SI>; | |
constexpr kilometres operator"" _km(long double d) noexcept { return kilometres(d); } | |
constexpr metres operator"" _m(long double d) noexcept { return metres(d); } | |
constexpr centimetres operator"" _cm(long double d) noexcept { return centimetres(d); } | |
constexpr millimetres operator"" _mm(long double d) noexcept { return millimetres(d); } | |
constexpr seconds operator"" _s(long double d) noexcept { return seconds(d); } | |
constexpr grams operator"" _g(long double d) noexcept { return grams(d); } | |
constexpr kilograms operator"" _kg(long double d) noexcept { return kilograms(d); } | |
using length = metres; | |
using mass = kilograms; | |
using time = seconds; | |
using speed = decltype(std::declval<length>() / std::declval<time>()); | |
} | |
namespace usa | |
{ | |
class USA; | |
using inches = define_length<USA, std::ratio<1, 12>>; | |
using feet = define_length<USA>; | |
using pounds = define_mass<USA>; | |
using seconds = si::seconds; | |
} | |
template<class FromS, class ToS> | |
struct conversion<unit<si::SI, 1, FromS>, unit<usa::USA, 1, ToS>, double> | |
{ | |
static constexpr double convert(double inches) noexcept | |
{ | |
// this only works for m -> inches at the moment | |
// also need to think about conversions from X -> SI -> Y | |
using div = std::ratio_divide<FromS, ToS>; | |
return inches * 39.3701; | |
} | |
}; | |
} | |
#include <iostream> | |
#include <vector> | |
int main() | |
{ | |
using namespace units::si; | |
constexpr auto l = 10.0_m; | |
constexpr auto area = l * l; | |
constexpr dim<double, void, void, void, void> ll = l/l; | |
constexpr auto l2 = area / l; | |
constexpr metres nMeters = 299'792'458.0_m; | |
constexpr seconds nSecs = 1.0_s; | |
//constexpr speed sum = nMeters / nSecs; // compiler don't like this much | |
std::cout << (nMeters / nSecs).value() << std::endl; | |
constexpr auto r = 3.14159_radians; | |
constexpr degrees d = r; | |
constexpr auto sumD = degrees(r) + d; | |
constexpr auto sumR = r + radians(d); | |
static_assert(millimetres(1.0_km) == 1'000'000.0_mm, ""); | |
static_assert(metres(3.0_km) == 3000.0_m, ""); | |
static_assert(kilometres(3.0_m) == 0.003_km, ""); | |
auto kms = 3.0_km; | |
metres ms = kms; | |
// TODO: << operator | |
std::cout << kms.value() << "km == " << ms.value() << "m" << std::endl; | |
usa::inches one_metre = 1.0_m; | |
std::cout << "1m == " << one_metre.value() << "in" << std::endl; | |
std::vector<int> v = { 1, 2, 3 }; | |
count<int> num = card(v); | |
switch (num.value()) | |
{ | |
case 3: | |
std::cout << "whoop whoop!" << std::endl; | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment