Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plain and system-specific dimensions #281

Closed
mpusz opened this issue May 11, 2021 · 10 comments
Closed

Plain and system-specific dimensions #281

mpusz opened this issue May 11, 2021 · 10 comments
Assignees
Labels
enhancement New feature or request

Comments

@mpusz
Copy link
Owner

mpusz commented May 11, 2021

Split the design to provide:

  • base_dimension and derived_dimension defined without the associated unit
  • system_base_dimension and system_derived_dimension defined in the terms of the previous ones and a system-specific base/coherent unit.
@mpusz mpusz added the enhancement New feature or request label May 11, 2021
@mpusz mpusz added this to the v0.8.0 milestone May 11, 2021
@mpusz mpusz self-assigned this May 11, 2021
@mpusz
Copy link
Owner Author

mpusz commented May 11, 2021

It may also help to address #211.

@kwikius
Copy link
Contributor

kwikius commented May 12, 2021

If I understand correctly the problem you are referring to, You can use a metafunction (traits class) rather than embed the unit in dimension template parameters. see #187 (comment)

@mpusz
Copy link
Owner Author

mpusz commented May 12, 2021

Thanks, @kwikius. Yes, I know I can use more definitions (type traits, template variables, functions...) to provide additional information about each dimension. However, one of the goals of this library to be able to specify stuff as simple as possible and I believe that one definition like we had until now is the way to go (also based on the feedback of my users). Otherwise, we will invent macros as most other libs do...

Actually, the problem I am referring to is not to change the definition of existing dimensions. Hopefully, they will remain as close as possible to:

https://github.com/mpusz/units/blob/43825a531fa91501277161fefd94c50a3cf0ea96/src/systems/si/include/units/isq/si/speed.h#L37-L38

What I want to achieve with this change is to improve the ISQ part:

https://github.com/mpusz/units/blob/43825a531fa91501277161fefd94c50a3cf0ea96/src/systems/isq/include/units/isq/dimensions/speed.h#L34-L35

The ISQ is about quantities and not specific units. Right now it is solved with template templates is the system definition. I would prefer it to be an inherent part of the library's framework and not use template templates to work around the fact that system dimensions are specified in terms of their coherent units.

As a result, I would like to have:

  • units
  • dimensions (no units at this point)
  • system dimension (dimension + unit) (I would love to name it a "reference" but the name is already taken and I am looking for a way to merge those two together)
  • quantities (system dimension + unit)

@JohelEGP
Copy link
Collaborator

You'll probably need to start by making the systems explicit. See https://godbolt.org/z/ajfrsajK1.

#include <type_traits>

consteval auto sysof(auto v) { return system(v); }

namespace units {
  struct generic_system {};
  consteval generic_system system(auto) { return {}; }
  struct dim_angle {};
  namespace isq {
    struct isq_system {};
    consteval isq_system system(auto) { return {}; }
    namespace si {
      struct si_system {};
      consteval si_system system(auto) { return {}; }
      struct dim_length {};
    }
  }
}

static_assert(std::is_same_v<decltype(sysof(units::dim_angle{})), units::generic_system>);
static_assert(std::is_same_v<decltype(sysof(units::isq::si::dim_length{})), units::isq::si::si_system>);

IIRC, you want non-system units for what's in code currently called equivalent dimensions, right? Because the indirection ensures that equivalent base dimensions have the same symbols and that equivalent derived dimensions have equivalent exponents.

You could look at https://godbolt.org/z/hfqja1 from #248 for inspiration. With system in hand, you are be able to make base dimensions downcastable in conjunction with its symbol too. However, you can't use Child in ADL just yet as it's incomplete. Have sysof wrap the types in std::type_identity for you and it should work.

You'll probably want a symbolof similar to sysof to get the symbol of base dimensions. Then the definition of si::dim_speed can be simplified.

struct dim_speed : isq::dim_speed<dim_speed, metre_per_second, dim_length, dim_time> {}; 
struct dim_speed : isq::dim_speed<dim_speed, metre_per_second> {}; 

But first, you need to deal with isq::dim_speed.

namespace units::isq {
 template<typename Child, Unit U, DimensionOfT<dim_length> L, DimensionOfT<dim_time> T>
 struct dim_speed<Child, U, L, T> : derived_dimension<Child, U, exponent<L, 1>, exponent<T, -1>> {};

You know dim_length and dim_time, so you can craft a way to get their symbol. Perhaps with a dummy unit, potentially doubling all instantiations. But maybe the refactoring will make this simpler, if their Unit U template parameter are something you want to remove or delay to system dimensions.

Then you might end up with something like this:

namespace units::isq {
 template<typename Child, Unit U>
 struct dim_speed<Child, U, L, T> : derived_dimension<Child, U, exponent<downcast_base_dimension<symbol-of-length-dimension, sysof<Child>()>, 1>, exponent<downcast_base_dimension<symbol-of-time-dimension, sysof<Child>()>, -1>> {};

Does that last line looks like what you want to achieve?

@mpusz
Copy link
Owner Author

mpusz commented Jul 27, 2021

IIRC, you want non-system units for what's in code currently called equivalent dimensions, right? Because the indirection ensures that equivalent base dimensions have the same symbols and that equivalent derived dimensions have equivalent exponents.

Yes, I would like the "plain" dimension to scope only on the dimensional equation definition (symbol + exponents). With this, two system dimensions would be equivalent if they would have the same plain dimension. System dimension would add system-specific units to it. Note that the exponents may differ (i.e. area in some systems may be defined as "length[m] * length[mm]").

@mpusz
Copy link
Owner Author

mpusz commented Jul 27, 2021

You'll probably need to start by making the systems explicit.

I am not so sure about it and would prefer to not introduce yet another dedicated abstraction.

You'll probably want a symbolof similar to sysof to get the symbol of base dimensions.

Simplifying a definition could be nice but also can be limiting. For example, let's assume that we move dimensions based on angle to isq::si_angle system and leave isq::si system as it is officially specified by ISO. si_angle would only redefine "angular" dimensions. Those definitions would be based on existing SI dimensions without the need to redefine them in isq::si_angle namespace. Of course, we can nest namespaces to end up with isq::si:::angle for this particular case but I am not sure if that will work for other cases as well (user-defined systems). Also, nesting of namespaces (using more than one namespace at a time) may confuse ADL in some cases, right?

@JohelEGP
Copy link
Collaborator

Yes, I would like the "plain" dimension to scope only on the dimensional equation definition (symbol + exponents).

What's stopping you from doing just that?

You'll probably need to start by making the systems explicit.

I am not so sure about it and would prefer to not introduce yet another dedicated abstraction.

You want to decouple things that are inherently coupled here, because they carry more information that necessary. I don't see a portable way around this without reflection.

However, I believe you might be able to do away with it, too, once you've refactored enough that you don't need the dimensions in isq to be class templates. I think they could end up looking like struct x_dim { static constexpr auto symbol{...}; };

You'll probably want a symbolof similar to sysof to get the symbol of base dimensions.

Simplifying a definition could be nice but also can be limiting. For example, let's assume that we move dimensions based on angle to isq::si_angle system and leave isq::si system as it is officially specified by ISO. si_angle would only redefine "angular" dimensions. Those definitions would be based on existing SI dimensions without the need to redefine them in isq::si_angle namespace. Of course, we can nest namespaces to end up with isq::si:::angle for this particular case but I am not sure if that will work for other cases as well (user-defined systems). Also, nesting of namespaces (using more than one namespace at a time) may confuse ADL in some cases, right?

Are you pointing out that this might break sysof? For this case at least, it should hopefully work. ADL wouldn't find system() in si_angle and would instead look in the namespace of the template arguments of its bases, recursively so, until it finds some si dimension in base_dimension or an exponent. And if it's mixed, you get a compile-time error due to ambiguity.

@mpusz
Copy link
Owner Author

mpusz commented Jul 28, 2021

What's stopping you from doing just that?

Lack of time 😉 I didn't work on it too much yet but hopefully I will start soon. I like your suggestions and maybe we will go this way. I need some time to experiment...

@JohelEGP
Copy link
Collaborator

Makes sense, considering that everything can be solved with another level of indirection (or so they say, don't quote me on that). I was wondering if you had run into problems implementing that, actually.

@mpusz
Copy link
Owner Author

mpusz commented Jun 15, 2023

Done in V2

@mpusz mpusz closed this as completed Jun 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants