c++ - programacion - ¿Cuál es la mejor manera de formar relaciones estándar std:: chrono:: durations and std::?
relaciones entre clases programacion orientada a objetos (1)
Estaba leyendo esta excelente respuesta que usaba las microfortnights
unidad de duración del tiempo cómico para ilustrar un buen punto de una manera memorable.
typedef std::ratio<756, 625> microfortnights;
std::chrono::duration<int, microfortnights> two_weeks(1000000);
Y se me ocurrió la pregunta:
Si realmente quisiera hacer esto (lo más probable es que tenga alguna otra duración no trivial, como el tiempo disponible durante un cuadro o durante los N ciclos de un procesador), ¿cuál es la mejor manera de hacerlo?
Sé que la ratio<N, D>
creará un tipo único asociado con cada valor de N
y D
Por lo tanto, la ratio<4, 6>
es un tipo diferente que la ratio<2, 3>
, aunque representan la misma fracción (reducida). ¿Siempre tengo que hacer los cálculos para simplificar el factor de conversión a términos reducidos?
Sería más conveniente escribir:
using microfortnights = std::chrono::duration<long, ratio<86400*14, 1000000>>;
en lugar de:
using microfortnights = std::chrono::duration<long, ratio<756, 625>>;
Pero entonces estos serían dos tipos diferentes, en lugar del mismo tipo. La primera expresión es más fácil de inspeccionar para la corrección. Pero hay muchas representaciones de esta fracción, y la segunda es posiblemente canónica, y por lo tanto preferida. Si tengo demasiados tipos vagando por mi programa que en realidad representan la misma unidad, entonces eso puede llevar a un aumento innecesario del código de la plantilla.
A continuación, estoy ignorando los espacios de nombres con el interés de ser conciso. duration
está en el espacio de nombres std::chrono
, y la ratio
está en el espacio de nombres std
.
Hay dos buenas maneras de asegurarse siempre de que su ratio
se reduzca a los términos más bajos sin tener que hacer la aritmética. El primero es bastante directo:
La formulacion directa
Si solo quiere saltar directamente a las microfortnights
, pero sin tener que darse cuenta de que la fracción reducida de 86,400 * 14 / 1,000,000 es 756/625, simplemente agregue ::type
después de la ratio
:
using microfortnights = duration<long, ratio<86400*14, 1000000>::type>;
El type
anidado de cada ratio<N, D>
es otra ratio<Nr, Dr>
donde Nr/Dr
es la fracción reducida N/D
Si N/D
ya está reducido, entonces la ratio<N, D>::type
es del mismo tipo que la ratio<N, D>
. De hecho, si ya hubiera descubierto que 756/625 era la fracción reducida correcta, pero era paranoico al pensar que podría reducirse aún más, podría haber escrito:
using microfortnights = duration<long, ratio<756, 625>::type>;
Entonces, si tiene alguna duda de que su ratio
se expresa en los términos más bajos, o simplemente no quiere que le molesten con la verificación, siempre puede agregar el ::type
a su tipo de ratio
para estar seguro.
La formulación verbosa.
Las unidades de duración de tiempo personalizadas a menudo aparecen como parte de una familia. Y a menudo es conveniente tener toda la familia disponible para su código. Por ejemplo, las microfortnights
están obviamente relacionadas con fortnights
, que a su vez están relacionadas con weeks
, que se derivan de days
, que se derivan de hours
(o de seconds
si lo prefiere).
Al construir a su familia una unidad a la vez, no solo hace que toda la familia esté disponible, sino que también reduce la posibilidad de errores al relacionar a un miembro de la familia con otro con la conversión más simple posible. Además, hacer uso de std::ratio_multiply
y std::ratio_divide
, en lugar de multiplicar los literales también significa que no tiene que mantener insert ::type
todas partes para asegurarse de mantener su ratio
en los términos más bajos.
Por ejemplo:
using days = duration<long, ratio_multiply<hours::period, ratio<24>>>;
ratio_multiply
es un nombre typedef al resultado de la multiplicación ya reducida a los términos más bajos. Así que lo anterior es exactamente el mismo tipo que:
using days = duration<long, ratio<86400>>;
Incluso puede tener ambas definiciones en la misma unidad de traducción, y no obtendrá un error de redefinición. En cualquier caso, ahora puedes decir:
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>>;
Y hemos terminado con un nombre de typedef para las microfortnights
que es exactamente el mismo tipo que en nuestra formulación directa, pero a través de una serie de conversiones mucho más simples. Aún no tenemos que preocuparnos por reducir las fracciones a los términos más bajos, y ahora tenemos varias unidades útiles en lugar de solo una.
También tenga en cuenta el uso de std::micro
en lugar de std::ratio<1, 1000000>
. Este es otro lugar para evitar errores por descuido. Es tan fácil (al menos para mí) confundir (y leer mal) el número de ceros.