Skip to content

Instantly share code, notes, and snippets.

@Porges
Last active January 21, 2016 09:01
Show Gist options
  • Save Porges/8a24d8fff9e5e6c01038 to your computer and use it in GitHub Desktop.
Save Porges/8a24d8fff9e5e6c01038 to your computer and use it in GitHub Desktop.
#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