I was reading this excellent answer which used the comical time duration unit
typedef std::ratio<756, 625> microfortnights;
std::chrono::duration<int, microfortnights> two_weeks(1000000);
If I really wanted to do this (more likely some other non-trivial
duration such as the time available during a frame, or during N cycles
of a processor), what is the best way to do this?
using microfortnights = std::chrono::duration<long, ratio<86400*14, 1000000>>;
using microfortnights = std::chrono::duration<long, ratio<756, 625>>;
Below I'm ignoring namespaces in the interest of being concise.
duration is in namespace
ratio is in namespace
There are two good ways to always ensure that your
ratio is reduced to lowest terms without having to do the arithmetic yourself. The first is quite direct:
The direct formulation
If you just want to jump straight to
microfortnights, but without having to figure out that the reduced fraction of 86,400*14/1,000,000 is 756/625, just add
::type after the
using microfortnights = duration<long, ratio<86400*14, 1000000>::type>;
type of every
ratio<N, D> is another
ratio<Nr, Dr> where
Nr/Dr is the reduced fraction
N/D is already reduced, then
ratio<N, D>::type is the same type as
ratio<N, D>. Indeed, had I already figured out that 756/625 was the correct reduced fraction, but was just paranoid in thinking it might could be reduced further, I could have written:
using microfortnights = duration<long, ratio<756, 625>::type>;
So if you have any doubt that your
ratio is expressed in lowest terms, or just don't want to be bothered with checking, you can always append the
::type to your
ratio type just to be sure.
The verbose formulation
Custom time duration units often pop up as part of a family. And it is often convenient to have the entire family available to your code. For example
microfortnights is obviously related to
fortnights, which in turn is related to
weeks, which is derived from
days, which is derived from
hours (or from
seconds if you prefer).
By building up your family one unit at a time, you not only make the entire family available, you also reduce the chance of errors by relating one family member to another with the simplest possible conversion. Additionally, making use of
std::ratio_divide, instead of multiplying literals also means you don't have to keep insert
::type everywhere to ensure that you keep your
ratio in lowest terms.
using days = duration<long, ratio_multiply<hours::period, ratio<24>>>;
ratio_multiply is a typedef-name to the result of the multiplication already reduced to lowest terms. So the above is the exact same type as:
using days = duration<long, ratio<86400>>;
You can even have both definitions in the same translation unit, and you will not get a re-definition error. In any event you can now say:
using weeks = duration<long, ratio_multiply<days::period, ratio<7>>>; using fortnights = duration<long, ratio_multiply<weeks::period, ratio<2>>>; using microfortnights = duration<long, ratio_multiply<fortnights::period, micro>>;
And we have ended up with a typedef-name for
microfortnights that is the exact same type as in our direct formulation, but through a series of much simpler conversions. We still do not have to be bothered with reducing fractions to lowest terms, and we now have several useful units instead of just one.
Also note the use of
std::micro in place of
std::ratio<1, 1000000>. This is another place to avoid careless errors. It is so easy (at least for me) to mistype (and misread) the number of zeroes.