
From: "Andy Little" andy@servocomm.freeserve.co.uk
"Terje Slettebø" <tslettebo@broadpark.no> wrote in message
I've been playing with a library like this, a year or so ago, too, but saw there were several proposals in that area, which worked similarly, and decided instead to focus on the Concept Traits library. I decided that I could then instead support other people's proposals in the area of quantity/units library.
I've always seen this kind of library as a "fun" thing, :) as I'm interested in science, having "played with" electronics, physics, chemistry, etc. especially in my youth, and I'm sure such a library - of Boost standard (using modern C++), which this one seems to be - would be a great thing for scientists, and people interested in science, everywhere.
The 'fun' thing is a key point of the library. Another useful feature is consistency in converting between units, as well as the fact that each type encodes a lot more information than a double, making it a C++ 'hub' for conversion to-from eg XML , or the same entity in other programming languages, data exchange , COM, CORBA etc. (Thats a theme I would like to pursue in other areas such as graphics too) I hope to get to get to grips with the serialisation library when the next boost distro comes out and
Some of the issues I ran into, when playing with such a library, were: How to handle input/output, especially formatting the prefix and unit information. For example, one may write the same unit as: 1 MJ 1000 kJ 1000000 J 1 megajoule 10^6 J 10^6 kg·m^2·s^-2 10^6 kg·m^2/s^2 etc. In other words, it may be written in an infinite variety of forms. Moreover, the user may want the various forms in different contexts. Thus, I/O was one of the areas I found most complex to solve. The unit calculation itself is really a solved problem. :) (at least for the usual SI units). Because of this, it may be best to decouple the presentation from the unit calculation. One may provide some convenience output components, but it may be best if it's also possible for the user to provide the formatting in any way they want. For example if they want to show it using a GUI widget. So one may have functions to retrieve the components of the quantity, i.e. the numerical value, the unit (or unit components in the fundamental SI units) and their exponent, etc. If one provide convenience functions, then there's an issue of if one should provide names for both char and wchar_t, and how to deal with output of units that have no ASCII/Latin1 character, such as ohm. Looking at the docs, it seems that this issue has been considered, and that it's possible to provide custom output. Could you perhaps have shown how to output some of the formats above, given the quantity 1 MJ? I've mentioned SI, here. That should be an "alarm bell", because another issue to consider is if the library should be SI specific, or be able to handle units in general, including user-supplied ones. I remember this has been discussed in earlier discussions about quantity libraries, here, and one "obvious" candidate for non-physical units is money: You may have a numerical value, and its currency, and the possibility to convert between the various currencies. Your library is named "physical quantities library", and this points to it being more SI-specific; looking at the implementation, it seems the number of dimensions is hardcoded to 9. I'm not saying this is right or wrong, merely mentioning that - apart from the I/O issue - this was another major headache I had in the design of the library I worked on. The headache was this: It would be possible to accommodate for an arbitrary number of dimensions (for example using typelists or type vectors - see Dave Abraham and Aleksey Gurtovoy's article "A Deeper Look at Metafunctions" (http://www.artima.com/cppsource/metafunctions.html) for an example of this. I used a similar approach, but without the mpl::transform part, as my early version was Loki-based (I had a custom "transformer"). Had I done it today, I'd use MPL), but then one question is what the cost may be in terms of compilation time. Using type vectors rather than typelists might be faster, but necessarily; I tried to substitute mpl::vector for mpl::list in the Concept Traits library unit test, and watched the compilation time go through the roof... On the other hand, if one stick to a fixed number of dimensions, how many is enough? And what should they be? If we were to get a quantity library in Boost, I think it should be able to handle other quantities than the physical ones, as well (I didn't think of the money example in the previous post, so I couldn't think of a non-physical quantity example, but I was concerned about the library being SI-specific. As I said above, this might be ok, but should be something that should be explicitly addressed, perhaps in the FAQ). One sticky issue if you allow arbitrary units/dimensions, is that in SI, the only base unit with an exponent is "kg" (due to "historical accident), so 1000 kg isn't 1 kkg but 1 Mg. This has to be accommodated. Because of issues like this (particularly the compile time/flexibility concern), and seeing that there were several other approaches in existence for this, I was content to step aside, and watch them struggle with these issues, instead. :) Just something to chew on. :) It's probably nothing new, but I think these are issues that needs to be addressed for a library to be considered for Boost. try
to implement some of this.
I guess this addresses the I/O, also.
The other aspect is simply that of modelling. It helps to visualise the entity you are modelling when the physical quantities are named. That is difficult to describe rationally but certainly helps me when I am coding. The fun aspect translates into much better understanding of what source code is actually doing.
Absolutely. When I played with my library, it was rather fun to write, e.g. (to use the syntax in your library): length::m m=123; mass::kg kg=234; time::s s=10; std::cout << kg*m*m/(s*s); and get the output: 35.40186 kJ :) In other words, it told you what the named unit was, if it existed. The output was done by having some stream state decide the formatting. However, as mentioned, this was rather experimental: I ended up using a formatting string, for more or less maximum flexibility, i.e. "%v %p%u<n>", where <n> was a number from 0 and up. 1 would give the output in the base units ("kg·m^2·s^-2"); 2 would give the next "level" up ("N·m"), and 3 would give the level after that (if existed) "J". %v gives the numerical value (excluding the prefix value), "%p" gives the prefix - "k" here, and "%u" gave the unit, "J". I had "tons" of these format specifiers, so I could get "35.40186 kilojoule", etc., as well. To sidestep this "implicit"/stream-stored formatting, it might be better to use casting, as you use in the library, provided that you can then print the resulting quantity any way you want. Oh, and one other thing to consider: The handling of temperature. For example, will you be able to accommodate different temperature scales?: temperature::K t=283.15; std::cout << (temperature::C) t; // Prints "10 °C", or some such. Note that for Fahrenheit, it's not just an offset calculation, but also the degrees are different. Some of this stuff is what makes making a quantity library both challenging and fun. ;) (Like I said in the previous posting, you're talking with someone who have struggled with these issues, so I guess I have some idea of some of the thorny issues it may raise. ;) )
I tried to test the library on Intel C++ 7.1 (electronics_example.cpp, picked at random) but got some errors which prevented the test, so I've tried it on g++ 3.2, instead, where it worked. Unless I haven't found it, you might want to include which compilers/versions it works for in the docs. If you're interested in the diagnostics from Intel C++, I can mail them to you.
Yes I would be very interested in that naturally, maybe a boost post would be most useful?
There are many of the same kind, so sure: Several of each of these: electronics_example.cpp PQS-2-00-02\pqs/numeric/converter_exception.hpp(24): warning #811: exception specification for implicitly declared virtual function "boost::numeric::bad_numeric_conversion::~bad_numeric_conversion" is incompatible with that of overridden function "std::bad_cast::~bad_cast" class bad_numeric_conversion : public std::bad_cast ^ PQS-2-00-02\pqs/meta/rational_c.hpp(84): error: the type of partial specialization template parameter constant "N" depends on another template parameter IntegerType N, ^ PQS-2-00-02\pqs/ct_quantity/named_abstract_quantity/anonymous_abstract_quant ity/length_pwr.hpp(76): error: constant "N2" is not used in template argument list of class template "pqs::meta::binary_operation<pqs::length_pwr<N1, D1>, Op, pqs::meta::rational_c<RatValueType, N2, D2>, void>" int N2, ^ Then this: E:\PROGRAM\C++\PQS-2-00-02\pqs/ct_quantity/named_abstract_quantity/anonymous _abstract_quantity/length_pwr.hpp(66): error: class "boost::mpl::void_" has no member "numerator" base_type::numerator, ^ detected during: <long instantiation stack trace snipped> Then some smaller stuff: PQS-2-00-02\pqs/ct_quantity/ct_quantity.hpp(116): remark #424: extra ";" ignored }; ^ Several of these: PQS-2-00-02\pqs/ct_quantity/quantity_unit/si_prefix.hpp(65): warning #1334: the "template" keyword used for syntactic disambiguation may only be used within a template si_unit::prefix<si_unit::yotta>::template symbol<char>() ^ I'll send you the whole thing on mail, as well. Note that although Intel C++ is EDG-based, the Windows version (which I used) is a little "schizophrenic", as it tries to be "compatible" with MSVC, too, especially if you play with some of the (undocumented, for Intel C++) EDG switches, to try to make it more conformant. I used the following: --arg_dep_lookup --no_microsoft_bugs --no_guiding_decls However, even when not using any of these switches, it gives the above errors. As EDG (which Intel C++ uses) is considered one of the most conforming compilers there is, I think it makes sense to at least compile on an EDG compiler, too.
It has only been tested on VC7.1 and gcc3.2.( As I understand it the base level for a boost proposal is two compilers)
I haven't seen that explicitly, but my experience is that for more regular stuff, two compilers might be enough, for more tricky/advanced stuff, one should test on at least three. The Concept Traits library compiled and ran fine on Intel C++ 7.1 and MSVC 7.1, but bombed on g++ 3.2, so even if it works on two compilers, that's no guarantee for the third (even for the first two, having developed it on Intel C++, I had to tweak it a little to make it work on MSVC 7.1).
What might help is to give an overview of other libraries that exist for this, and what makes PQS different. One place to look is the Boost Files section (I saw at least three such libraries there).
This is a tricky area, as I have already spent considerable time criticising other units libraries.
Unless I've missed it, maybe this could have been included in a section in the docs, too? This gives other people an overview of how your library stands in comparison to these other proposals, maybe as well as the rationale for the choices. I realise this can be quite a lot of work (and as you said, you've done a lot of work in this area, already, although perhaps not documented it to a large degree), but as a library author, if you want to "sell" your library, I'm afraid you'll have to do this, too, just like salespeople need to be aware of the competition (if any), and be able to explain what makes their product better, to make a successful sale. :) Regards, Terje