-
Notifications
You must be signed in to change notification settings - Fork 87
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
Poll: UDLs vs constants #48
Comments
Related issue #31. |
UDLs are simple shorter and neater so get my vote . Ultimately if they have to have slightly longer names then that is not a problem as far as I am concerned. A nice solution would be to extend UDL rules to allow qualified lookup. Here is one possible syntax. I think scope-resolution-operator followed by a number is syntactically available auto x1 = si::1mm;
auto x2 = si :: 1 mm; // with the scope resolution operator prefix
// pretty spaces should be parsable That would bring them to a similar functionality to inline constants. EDIT: Actually lthe above syntax could mean that if si::mm is a type with an explicit constructor taking an int, construct a constexpr object of that type with that argument. No literal operator required! Meanwhile the obvious pragmatic workaround is to attach a postfix namespace to avoid collisions which are otherwise inevitable as use grows auto x2 = 1l_si; |
Qualified lookup for UDLs was discussed already in the ISO C++ Committee but if I recall correctly it did not fly (at least so far). But even if it did it would solve only point 3 from the above list. Still, we cannot easily create some UDLs and long names do not help in some cases (i.e. But yes, I also always hated the "multiply" syntax to form quantities but it seems it actually may have some benefits over UDLs. |
There is another option. Just use a temporary as you show above, length::mm{1}
// or ...
length::mm<>(1) // to expose the value_type default to double Anyway, certainly worth looking at where and how often the use of such constants occurs in real world. How common is it and is it necessary to define constants at global scope versus just temporaries with explicit initialisers as I used to do before UDLs? (N.B. Discussing quality of my code and how it could be improved is not the point here ;). It is just shown as an example of day to day use. The code could certainly benefit by review, upgrading to UDLs etc,etc) To answer point1 I would take the UDL where it works and use the temporary where not I think using 1 * si::mm here would start to get on my nerves! in for loop you would hope for a terse syntax. for ( auto t = 0ms ; t <= timeout; ++t ){ //nice when it works !
for ( auto t = 0 * si::ms ; t <= timeout; ++t ){ // maybe !
for ( auto t = si::0ms ; t <= timeout; ++t ){ // maybe !
In class constructors generally I found explicit init with an explicit value easiest), so UDL irrelevant Another answer : Provide both options for now ... :) |
Actually I am starting to change my mind. Maybe the multiplier syntax is not so bad :) |
Hehe, I see you are going the same path as I did. My initial opinion was also "yack, it is an awful and old way to do it" ;-) |
With constants, we could also consider more extensions like: inline constexpr auto km = k * m;
inline constexpr auto kmph = km/h; But as we discussed in another thread that will work against the downcasting facility as the user is never providing a user-friendly name for a target type. |
Won't these expressions produce km×h instead of km/h? |
@i-ky You are right, I do not have experience with this syntax and did not notice that. But it seems it is a good point against the "multiply" syntax. I will fix the code sample in polls above, thanks! |
Another point against UDLs. Arbitrary expressions are not allowed auto x = (1./4)sq_m; // error
But a major point against constants is that they clog up the global namespace with many tiny names , h, s, etc. and that could be a real headache in real scale code . auto constexpr v1 = 10 * m/s;
std::cout << v1 << '\n';
double constexpr s = 2.0;
auto v2 = 20 * m/s;
std::cout << v2 << '\n'; That doesnt happen with UDLs'. |
Personally I dont think item 1 regarding initialisation of quantitites by runtime numeric values on the list above is a problem in practise. Generally a variable quantity is initialised with a constant , but once initialised, it is the quantity that is being used as the runtime variable. Where the UDL syntax lacks recommend to use a temporary. In light of my point about name hiding in the post above, I think that UDLs are the right way to go. Since it is not possible to use qualified lookup in current c++, the only other option is to add a namespace to the name itself as in the good old C days. So for example reserve the Q_ prefix for use by std::quantity literals auto v1 = 10.0Q_F;
auto v2 = 10.0Q_J;
auto v3 = 10.0Q_W;
auto v4 = 10.0Q_K;
auto v5 = 10.0Q_d;
auto v6 = 10.0Q_l;
auto v7 = 10.0Q_L;
auto v8 = 10.0Q_erg;
auto v9 = 10.0Q_ergps; EDIT: in line with everything is std being lowercase, and also becase it is easier to read against the preceding digit. Anyway the style has a lot of appeal to me as I test it. The uniform prefix lets you know immediately that the type is a quantity, rather than for example a chrono duration or another name for a long auto v1 = 10.0q_F;
auto v2 = 10.0q_J;
auto v3 = 10.0q_W;
auto v4 = 10.0q_K;
auto v5 = 10.0q_d;
auto v6 = 10.0q_l;
auto v7 = 10.0q_L;
auto v8 = 10.0q_erg;
auto v9 = 10.0q_ergps; That solves all items. I think it is correct that the si units take precedence over the other less complete unit systems. SI is the dominant system for very good reason. For disambiguating non si units, then just extend the namespace. The extra ugliness is justified: auto v10 = 10.0Q_imp_in; |
I think we need feedback from a wider audience on this. It might make sense to retain both options. |
I think Also, during the last ISO discussion it was raised that there should be a dedicated operator: quantity<Dim, U, Rep> operator*(Rep v, magic_type<Dim, U>); It will only allow the syntax inline cosntexpr magic_type<dim_time, second> s;
I think that both options seem interesting and thus we will probably provide both for now and get some more field experience with both of them. Do you have any suggestions on how to name a |
Both options are the way to go. Sometimes you want to encode a constant, others, do I like I make a point for having a natural syntax when writing formulas with a units library at nholthaus/units#196. In C++20, you can write Looking for formulas at https://en.wikipedia.org/wiki/Formula, we see: I think |
On a second thought, there'd be Another advantage of constants relates to the N*M problem. The library only offers |
A major practical problem with global constants is they can be hidden by local constants of the same name : // some short name global constant
double constexpr s = 0.5;
//----------------------------------
#include <iostream>
int main()
{
constexpr auto a=1.,b=2.,c=3.,s=0.,k=5.;
double y = 1;
auto z = y / s ; //<< --- ouch!
std::cout << z << '\n';
} |
In PQS I solved the problem of ad-hoc units by creating a so-called unit_binary_op that models the pqs::unit concept while providing customised output Here is how an ad-hoc quantity can be created in source code output : PQS fountain power example I show si units there but you can also use non-si /si combination of course and also stack them together |
There's also the case of linear algebra and other quantity wrappers. using namespace units::physical::si;
fs_vector<si::length<si::metre>, 3> v = { 1_q_m, 2_q_m, 3_q_m };
fs_vector<si::length<si::metre>, 3> v = fs_vector<int, 3>{ 1, 2, 3 } * 1_q_m;
fs_vector<si::length<si::metre>, 3> v = fs_vector<int, 3>{ 1, 2, 3 } * m;
auto v = fs_vector<int, 3>{ 1, 2, 3 } * 1_q_m;
auto v = fs_vector<int, 3>{ 1, 2, 3 } * m;
si::length<si::metre, fs_vector<int, 3>> v(fs_vector<int, 3>{ 1, 2, 3 }); Do you think unit constants are an improvement here? |
This can be mitigated by using the dimension concepts. Another reason to standardize them. |
The more I think about it the more I think that UDLs may not be the right tool for the job. Yeah, we used it in
std::chrono
and it seems nice to writeauto v = 120km / 2h;
. However, they could be replaced with constants. For example:Here are a few issues that I see with UDLs:
UDLs are only for compile-time known values. Currently with UDLs:
with constants those 2 cases would look like:
Constants treat both cases in a unified way. It is also worth to notice that we work mostly with runtime variables and compile-time known values appear only in physical constants and unit tests.
UDLs for some units may be impossible to achieve in C++. I already found issues with
F
(farad),J
(joule),W
(watt),K
(kelvin),d
(day),l
orL
(litre),erg
,ergps
. It is probably still not the complete list here. All of those problems originated from the fact that those numeric symbols are already used in literals (sometimes compiler extensions but still). I am afraid that at for some of those cases we will not be able to fix it or workaround it in the C++ specification. None of those issues affect constants.UDLs cannot be disambiguated with the namespace name:
With constants it is simple:
So maybe we should prefer constants over UDLs? If so should they be put to an inner inlined namespace (like
literals
)?Let's vote:
(please vote only once by clicking on the correct bar above)
The text was updated successfully, but these errors were encountered: