
David Walthall wrote:
Oleg Abrosimov wrote:
Actually, my question is: Can you provide good reasons to follow current PQS style (length::m etc.) instead of simply Length, as me and others have proposed?
I can think of a couple. Here is a rather contrived one:
pqs::length universe_radius = 1.4e26 * METER; pqs::volume universe_volume = (4.0/3.0) * M_PI * pqs::pow<3>(universe_radius);
You now have 2.744e78 m^3, which could overflow if the maximum exponent of the double on your system is +37. As I said, that was contrived, but embedded systems may not have a large maximum exponent for doubles.
Under/overflow will certainly be an issue for some people if they can't specify their own base units, but I don't know if a tight coupling between units and dimensions is the best solution. Could there be some sort of global setting (a facet, maybe?) where we could specify units for base dimensions for the entire application?
The layer that I work on has to collect data from several legacy application outputs that use different underlying systems of units. One gives data in kJ and another gives data in kcal. Rather than having to constantly convert between the two values, which is error prone, I simply write:
// Prototypes of interface with two different legacy codes pqs::energy::kJ GetLegacyData1(/*args*/); pqs::energy::kcal GetLegacyData2(/*args*/);
// used in my program pqs::energy::kJ = GetLegacyData1() + GetLegacyData2();
Another example would be something like this (excuse the psuedo-pqs): pqs::pressure::psi WaterPressure(pqs::length::feet depth) { const pqs::length_per_pressure::foot_per_psi pressureRatio(2.31); return depth/pressureRatio; } If units weren't specified in the function declaration, it could be called with the wrong ones, and its return value would be garbage. Both your example and mine have a common element though. They both involve moving between numeric values and dimensions. I can't think of an instance where units would be useful when that isn't the case. If that's true, then it seems like overkill to require bound units throughout the program. A simpler solution would be to prohibit dimension variables from being assigned or returning "raw" numbers. The only way to get or set a value would be through unit functions like this pqs::energy energyVal = pqs::kcal(value); My water pressure example would look like this pqs::pressure WaterPressure(pqs::length depth) { const pqs::length_per_pressure ratio(pqs::foot_per_psi(2.31)); return depth/ratio; } The value, 2.31, would be converted to the global units, whatever they happened to be, so the function would work with any unit system. Since units would be specified at the point of conversion, this method might even be a little safer. In pqs, you might do something like this pqs::length::m depth; // lots of code double depthAsDouble(depth); SetFieldValue("DepthInFt", depthAsDouble); cout << "Enter new depth (in feet): " cin >> depth; Because depth is declared far from where it's output and reassigned, these mistakes would be easy to miss. But if the code worked like this pqs::length depth; // lots of code // Must use units function to convert to double double depthAsDouble = pqs::feet(depth); SetFieldValue("DepthInFt", depthAsDouble); cout << "Enter new depth (in feet): " // Must use units function to convert from double cin >> depthAsDouble; depth = pqs::feet(depthAsDouble); The programmer would have to declare the units at the point where they're used. This wouldn't eliminate mistakes altogether, but it might discourage the more obvious ones. The biggest advantage of all this is that units would no longer be an integral part of the dimensions library, and the only difference between the unitless and unitful versions of the library would be in how values are assigned to and extracted from dimension variables.