mp-units icon indicating copy to clipboard operation
mp-units copied to clipboard

feat: one derived_unit to rule them all

Open JohelEGP opened this issue 5 years ago • 4 comments

In Rethinking the Way We Do Templates in C++ even more - Mateusz Pusz [ C++ on Sea 2020 ] (slides, pages 174-184), @mpusz presents an interesting problem of unifying all *_derived_units in a single interface in a future C++. I'd like to explore doing it today (with C++20, after this week's priorities).

JohelEGP avatar Sep 09 '20 06:09 JohelEGP

Sure, you are welcome to try :-)

mpusz avatar Sep 09 '20 06:09 mpusz

I have tried this twice. Twice stumbled unto too many compiler bugs using edge constexpr stuff.

I think it'd be easier if everything were an NTTP. That's too far-reaching for me delve into, in terms of code changes. It also has uncertain changes in how we view the specializations in compiler errors.

If the only purpose were to make the inheriting interface more uniform, I've thought that we could keep the NTTP limited to that:

struct metre : unit<metre, {dim_length{}, "m", si_prefix{}}> {};
struct kilometre : unit<kilometre, {kilo{}, metre{}}> {};
struct yard : unit<yard, {dim_length{}, "yd", ratio(9'144, 10'000)}> {};
struct mile : unit<mile, {dim_length{}, "mi", 1'760 * yard::ratio}> {};

struct second : unit<second, {dim_time{}, "s", si_prefix{}}> {};
struct second : unit<second, {hour{}, "h", 3600 * second::ratio}> {};

struct metre_per_second : unit<metre_per_second, {dim_speed{}}> {};
struct kilometre_per_hour : unit<kilometre_per_hour, {dim_speed{}, kilometre{}, hour{}}> {};
struct mile_per_hour : unit<mile_per_hour, {dim_speed{}, mile{}, hour{}}> {};

Where the NTTP argument is constructible from all variations needed of units.

The energy spent on this, coupled with the complexity of constexpr, has left me unable to open compiler bugs regarding the walls I hit.

1613589526

JohelEGP avatar Feb 17 '21 19:02 JohelEGP

Yeah, I've also bounced from this a few times already. NTTP is probably not the way to go. If anywhere, I think that NTTPs could be used to define dimensions. But we would lose the downcasting facility with such an approach.

mpusz avatar Feb 18 '21 18:02 mpusz

The current implementation of downcasting variables from the CE link at https://github.com/mpusz/units/discussions/248#discussion-3243038 gets pretty close. All it's missing are the unit constructors that resolve which type of unit to construct based on the arguments.

inline constexpr unit second = downcast_child_ref<second, named_unit{systems::si, "s"_s, prefix}>;
inline constexpr unit yoctosecond = downcast_child_ref<yoctosecond, prefixed_unit{yocto, second}>;
inline constexpr unit zeptosecond = downcast_child_ref<zeptosecond, prefixed_unit{zepto, second}>;
inline constexpr unit minute     = downcast_child_ref<minute, named_scaled_unit{"min"_s, no_prefix, ratio(60), second}>;
inline constexpr unit hour        = downcast_child_ref<minute, named_scaled_unit{"h"_s, no_prefix, ratio(60), minute}>;
inline constexpr unit day         = downcast_child_ref<minute, named_scaled_unit{"d"_s, no_prefix, ratio(24), hour}>;

inline constexpr unit metre = downcast_child_ref<metre, named_unit{systems::si, "m"_s, prefix}>;
inline constexpr unit yoctometre = downcast_child_ref<yoctometre, prefixed_unit{yocto, metre}>;
inline constexpr unit zeptometre = downcast_child_ref<zeptometre, prefixed_unit{zepto, metre}>;
inline constexpr named_scaled_unit astronomical_unit =
  downcast_child_ref<astronomical_unit, named_scaled_unit{"au"_s, no_prefix, ratio(149'597'870'700), metre}>;

inline constexpr unit gram = downcast_child_ref<gram, named_unit{systems::si, "g"_s, prefix}>;
inline constexpr unit yoctogram = downcast_child_ref<yoctogram, prefixed_unit{yocto, gram}>;
inline constexpr unit zeptogram = downcast_child_ref<zeptogram, prefixed_unit{zepto, gram}>;
inline constexpr unit tonne = alias_unit{megagram, "t"_s, prefix};
inline constexpr unit yoctotonne = prefixed_alias_unit{attogram, yocto, tonne};
inline constexpr unit zeptotonne = prefixed_alias_unit{femtogram, zepto, tonne};
inline constexpr unit centitonne = downcast_child_ref<centitonne, prefixed_unit{centi, tonne}>;
inline constexpr unit decitonne  = downcast_child_ref<decitonne, prefixed_unit{deci, tonne}>;

inline constexpr unit candela = downcast_child_ref<candela, named_unit{systems::si, "cd"_s, prefix}>;
inline constexpr unit yoctocandela = downcast_child_ref<yoctocandela, prefixed_unit{yocto, candela}>;
inline constexpr unit zeptocandela = downcast_child_ref<zeptocandela, prefixed_unit{zepto, candela}>;

I don't like that all variables are of type unit, though. named_units could be 32 bytes, but unit is 120 bytes. I won't mix these efforts, though. I'm not convinced that removing the explicit type of unit is expressive enough.

JohelEGP avatar Mar 12 '21 07:03 JohelEGP

Done in V2

mpusz avatar Jun 15 '23 06:06 mpusz