
"Leland Brown" wrote
Andy Little <andy <at> servocomm.freeserve.co.uk> writes:
"Beth Jacobson" wrote
Andy Little wrote:
In PQS the units are part of the type. IOW the numeric value and its unit are always tightly coupled in code. That is a powerful feature. Once the numeric part of a quantity is disassociated from its unit then manual checking and external documentation is required, which is often the current situation wherever doubles are used to represent quantities. Manual checking doesnt always work as well as intended... That of course is why the Mars lander crashed!
Agreed! Any code containing a numeric literal for a physical quanity, or any code that inserts or extracts a numeric value from a quantity should be required to also specify the units of the numeric value. That's a critical part of the goal of the PQS library.
I agree that unit checking can be an extremely useful feature, I'm just not convinced that tight coupling is necessary to achieve that.
But I also agree here. The goal can be achieved without necessarily making the units part of the type. I'll explain.
There is planned another quantity where you can change the units at runtime FWIW.
I mentioned much earlier that my own implementation of a quantities library was more like the so-called "t2_quantity" - at least the way I envisioned the t2_quantity. But as I read the discussion it occurs to me that my idea may not be the same as yours, and it might be useful for me to share what I have in mind. I hope it will shed some more light on the "length::m vs. length" debate.
A couple of people have suggested a syntax more like this:
length d = 5 * meter; speed v = 2 * inch / second; time t = d / v; cout << "time = " << t / minute << "minutes";
I also like this usage - this is how my library works. As you've pointed out, Andy, this is trivial to achieve with the current PQS implementation, by using:
typedef pqs::length::m length; typedef pqs::time::s time; typedef pqs::velocity::m_per_s speed;
const length meter(1); const length inch(0.0254); const time second(1); const time minute(60);
The problem is, it also allows sloppy code like this:
length d(5); // no error; but units not clearly documented! speed v(2); // no error; but units not clearly documented!
so that the numeric values are now divorced from their units (which are defined above, in the typedefs), thus potentially leading to another Mars lander problem.
I should clarify here that the units are still there in the type although not visible in the source code std:cout << d <<' ' << v <<'\n'; should output something like 5 m 2 m.s-1
What I want is the syntax I illustrated but to enforce specifying units along with numeric values.
It occurs to me that perhaps this would work:
struct length : public pqs::length::m { length( pqs::length::m x ) : pqs::length::m( x ) { } operator=( pqs::length::m rhs ); }
The idea being so that you would say: length d = pqs::length::m(5); rather than length d(5); //Now an error ? Would you want to allow this: length d = pqs::length::mm(5); // initialise to 5mm converted to meters ? Then you would probably need this too: pqs::length::ft d1 = d; OTOH Should length d = pqs::length::mm(5); be an error ? I can see that deriving from the current t1_quantity (now called fixed_quantity in Quan, the successor to PQS) could be beneficial for various uses. One use might be to prevent any conversions from base units, so that you would be allowed to work in base_units only. I don't see that as a replacement of the current type but rather maybe a companion. Currently though my priority is to finish off the original concept. It seems to work quite well, as well as being as flexible as possible. IMO some of the concerns re conversions are theoretical rather than practical. It should be remembered that the current type is much better than using a double in the role of quantity for the reasons mentioned in the docs. The ability to convert between units makes the fixed-quantity useful in a much wider variety of situations than if it was restricted to base units only. It also allows certain calculations to be just as efficient when using SI quantities with other than base units, but you need to read the semantics part of the docs quite carefully to find out what they are! Anyway my feeling is that the conversion functionality gives the type a 'richness' that is satisfying for programmers to work with and I'm not going to give that up. :-)
etc. I don't think this is a big departure from the current PQS concept. It's just a simple extension, really, but I think a very helpful one. As a user I'd rather have something like this provided for me than to have to write the extension myself for each type - length, time, speed, energy, etc.
I'm still unclear about the exact semantics you are looking for.
The cost is that I may have more units conversions happening behind the scenes. But they only occur when dealing with the numeric values themselves - i.e., input/output. All my internal computations should be occurring with my types length, time, speed, etc. - which would be defined using a common set of units. For many users a little conversion cost in I/O is of no consequence, if most of the time is spent doing calculations on values in length, speed, etc. (Other users can use the existing types that are tied to units.)
One benefit is that I could write all my code to be independent of which units I choose, except where I need to specify a quantity numerically (in which case I must specify units, of course). If I later decide, say, the scale of my problem is more suited to eV than kJ (or if some of my code is reused in a differently-scaled application), I simply change the definitions for my "energy" class, etc., and recompile!
Why not just use templates template <typename Energy> void my_func( Energy e) { BOOST_STATIC_ASSERT((boost::is_convertible<Energy,pqs::energy::J>::value)); // use e } my_func(pqs::energy::k()); my_func(pqs::energy::eV()); As Beth Jacobson suggested, perhaps there could
be an easy way for the user to select these "base units" for the entire application.
To me, having types like "length," "speed," etc. is also easier to understand. It might be a better starting place for illustrating the library to new users. Here's an example of how I think about physical quantities: My desk has a length. "Length in meters" or "length in inches" are not properties of the desk - only length. That length can then be expressed numerically in different units.
I would prefer to say it can Only be expressed numerically if the units are known otherwise the number is meaningless. *Assuming* what the unit is, is where the mistakes occur. The SI is pretty clear that a quantity must always have its units close by. A "length" object
would represent the desk's length in a similarly abstract way, which could then be expressed in whatever units are desired for I/O. Of course, internally the computer has to represent the length as a value in some unit, but that would be encapsulated in the type and thus transparent to the user. The same internal units would be used for all quantities, thus avoidinng the overhead of type conversions (except on I/O).
If my assumption that unit conversions are only needed when numeric values are assigned to or extracted from dimensions is correct, then dimensions don't really need to know about units. The units functions would need to have a concept of default dimension units so they'd know what they were converting from or to, but the dimensions themselves wouldn't know or care.
Well said. I think this is the same as what I'm suggesting.
That would be the real benefit of this system. Reducing the number of conversions and making units explicit at the point of conversion may have some small benefit, but making the dimensions library essentially unitless seems like a major advantage.
Of course it's your library, and I'm not trying to dictate design. But if you're looking for a way to make the dimensions library essentially independent of units while retaining the unit safety of the current system, this might be a direction to consider.
Having used the fixed-quantity aka t1_quantity and multi_unit_quantity aka t2_quantity for a while, I have found them to be very flexible in the ways that they can be used. The fixed_quantity and all the different predefined quantities in the headers represent a compile time database of SI quantities and units and I have no doubt that this database will have a wide range of uses, but I will leave these up to the future user to explore as there is a lot of work involved in just getting the original library functionality completed satisfactorily. Hopefully I can get at least the CVS database for quan up onto Sourceforge in the next few days. http://sourceforge.net/projects/quan regards Andy Little