When reading <ratio> and <chrono> I tried to imagine a Length-type that protects against accidental conversion errors.
This is what I got:
#include <iostream>
#include <ratio>
using namespace std;
template<typename Scale>
struct Length {
long long val_;
Length(long long val) : val_{val} {}
Length() = default;
Length(const Length&) = default;
Length& operator=(const Length&) = default;
// conversion
template<typename Scale2>
Length(const Length<Scale2> &other)
: val_{ other.val_*(Scale2::num*Scale::den)/(Scale2::den*Scale::num) }
{ }
// access
long long value() const { return val_; }
};
typedef Length<ratio<1>> m;
typedef Length<kilo> km;
typedef Length<milli> mm;
typedef Length<ratio<1000,1094>> yard;
To be used like this
int main() {
km len_km = 300;
mm len_mm = len_km;
cout << " millimeter:" << len_mm.value() << endl;
cout << " m:" << m{len_km}.value() << endl;
cout << " yd:" << yard{len_km}.value() << endl;
}
And now I could add all the + and * operations to get really comfortable… 🙂
I wonder:
- Is there an easier access to the arithmetic facilities that
durationandtime_pointdefine in<chrono>anyway? Can I use those reduce the effort forLength? - The compile-time constant
(Scale2::num*Scale::den)/(Scale2::den*Scale::num)seems dangerous in the conversion constructor (fraction/underflow?), but I can not figure a better metaprogramming way, Any hints here?
For “mixed mode” arithmetic and comparisons you can take advantage of
common_type<T1, T2>::typefor defining return types.durationspecializescommon_typeto be the greatest common divisor of Period1 and Period2, where Period1 and Period2 are the tworatios involved in an arithmetic or comparison operation. You might use it like:Unfortunately you’ll have to reinvent how to get the greatest common divisor of two
ratios at compile time. Start with compile-time gcd and lcm meta-functions for unsigned long long.Hmm… or you might be able to base your specialization of
common_typeon the one already done forduration. You could reinterpret the resultantduration‘speriodas a scale factor for yourLength. I haven’t prototyped this, just an idea.Agreed.
durationhandles this with:I.e. you need to
enable_ifyourLengthconversion constructor such that it only exists if the conversion is exact (if you want to base your length on integral types). For the conversion to be exact, the conversion factor(Scale2::num*Scale::den)/(Scale2::den*Scale::num)must be computable without division (except division by 1). You can useratio_divideto do this division for you, and then the resulting denominator must be 1 (for an exact conversion).This is a great project for learning
ratio! Have fun! 🙂