
"Terje Slettebø" wrote
"Andy Little" wrote
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.
Of the 7 above 5 are doable (approximately) with the current setup via casting : casting to different units changes the actual value of the quantity. here 'e' has a numeric value of 1., casting to kJ changes the numeric value to 1000. os.setf(std::ios_base::fixed,std::ios_base::floatfield); os.precision(0); energy::MJ e(1); os << e << '\n'; os << energy::kJ(e) << '\n'; os << energy::J(e) << '\n'; os.setf(std::ios_base::scientific); os << energy::J(e) << '\n'; os << anonymous_cast(energy::J(e)) << '\n'; gives: 1 MJ 1000 kJ 1000000 J 1e+006 J 1e+006 kg.m+2.s-2 ... ( BTW The difference between named-quantities and anonymous-quantities is significant. Otherwise one could not differentiate (say) energy and torque. The anonymous_cast function removes the named-quantity connotations: pqs::energy::J e(1); pqs::torque::N_m t(1); os << e << '\n'; os << t << '\n'; os << anonymous_cast(e) <<'\n'; os << anonymous_cast (t) <<'\n'; gives: 1 J 1 N.m 1 kg.m+2.s-2 1 kg.m+2.s-2 There are various rules for when and why anonymous quantities pop up in calcs. ) ... as far as the megajoule version is concerned I have attempted to try to stay reasonably close to the SI output scheme, which rules this out as valid output. http://physics.nist.gov/Pubs/SP811/contents.html
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).
Yes It is the details of the thing that cause the headaches!
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.
This is implemented though probably needs clarification (I am aware that the docs are currently rambling) Firstly #include the "pqs/pqs.hpp" header brings in "the kitchen sink" including the convenience debugging output. (sadly missed adding angles in this header in pqs-2-00-02, so the "kitchen sink" will be a bit bigger next version) However in the "pqs/types/ " dir are two versions of each quantity header. The one with the _out.hpp suffix brings in the debug output for that quantity. Output does decompose into value and units, but is rather long winded to describe here. There are also separate rules for the so-called incoherent quantities. the
numerical value, the unit (or unit components in the fundamental SI units) and their exponent, etc.
This sounds like a good scheme, but is not currently implemented except for the numeric_value()... todo
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
The main issue for me with wchar_t is quite what to make of it. ref the recent "need for Unicode" thread on this. However I will need to deal with it, currently there are some undefined functions intended to fill out this aspect. 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?
Yes I guess I could. The docs need a complete restart !
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.
Its not SI specific, and it is possible to use other so-called anonymous-abstract-quantities ( so long as they conform to the AnonymousAbstractQuantityImplementation Concept), however by doing that you need to fill in a large amount of detail. For instance the default output cant be used as its entirely dependent on the unit system. There is no convenient user-interface etc, and you would need to set all this up, which starts to get pretty cumbersome. ( I use a database to generate the types/ headers) .. However the math functions and operational stuff etc will work ok. There is also still a small amount of stuff that is hard wired ( eg the exponent-base for units), and might be better done as a traits class of the unit
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.
Unfortunately the arts of alchemy have been lost .... Seriously one stumbling block is the sheer inconsistency. Currencies units cannot be relatively measured in any meaningul way, so conversions arent constant.
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
Basically the library is built around the SI system yes (7 'base units') . The rationale is simply that in practise IMO that is the major application area for this library. It is targetted at engineering and education , which is a large market with a low profile, rather than high level physics which has a high profile but is not a high volume market. Where the last ounce of performance is required you will probably be using inbuilt types and probably as I understand it Fortran. However if you want RAD then this library may be very helpful and some very quick tests indicate that it doesnt perform too badly compared with eg doubles, if you have access to an optimising compiler. 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...
There are a fair few issues here. One being simply that I don't know enough about mpl. However one issue is legibility, Its easier to comprehend and typesafe : typedef anonymous_abstract_quantity< length_pwr<1>, time_pwr<-2>, mass_pwr<1>, temperature_pwr<0>, // these can default... current_pwr<0>, substance_pwr<0>, intensity_pwr<0> > anonymous_abstract_quantity_type; than boost::mpl::vector< rat<1>, rat<-2> rat<1> rat<0>, rat<0>, rat<0>, rat<0>
anonymous_abstract_quantity_type;
However I am reasonably confident that an mpl::container could be fitted in to the library without much difficulty. Again anything will do as long as the requirements of the Concept currently called "AnonymousAbstractQuantityImplementation" (Ouch!) are met. Again more documentation required I guess. The other issue is my binary_operation type deduction scheme. If there is an alternative boost mechanism with this functionality I'll be happy to try it. BOOST_DECLTYPE may fit the bill... sfinae ?? Overall.... Potentially a lot of fun to be had here ;-)
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
Within a given physical-quantity-system there will be a fixed number of base-units Eg, It is possible to use eg {length, time, mass} as a complete system with the library. In fact I should get round to implementing this as it is relatively easy to do and would improve compile times significantly where the other base-units arent required. that
should be explicitly addressed, perhaps in the FAQ).
One sticky issue if you allow arbitrary units/dimensions, is that in SI,
Ok. Basically anything (any set of things) that conforms to the rules (Ok .. what are the rules? ;-) ) will fit. However, as you pointed out previously its the output etc thats tricky, once you move out of SI you will need to provide a lot of that functionality yourself, so the 'simple to use' aspect is lost. Its these details that take the time, not the actual dimensional-analysis part. 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. :)
;-) see the prefix-offset and the extent in the OfQuantityComponents concept Algorithm is in "pqs/ct_quantity/types/components/adjusted_coherent_prefix.hpp" heres a snippet of the source ( 'of_type' is OfNamedQuantityComponents Concept): (BTW extent can be negative eg reciprocal-time --> -1) enum{ adjusted_numerator = (CoherentExponent::numerator / of_type::extent) + of_type::prefix_offset }; I have struggled with the 'Defining your own types' part of the docs...its still not very good. So a lot more work is required. On the compile-time/run-time issue...There are two types currently, a compile time type with fixed units and a runtime type where units can be modified. I think there will also be a need for a universal_quantity, which can hold any type at runtime. This would probably be the basis of something to pass around among applications, ... and prototypes are around on the web, but I've lost the reference
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.
Thanks for the in-depth stuff, support and the thoughts..
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 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;
[snip] (explicit value_type ctor )... 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
:)
PQs used to do that, however it'll now return an anonymous-quantity here, because dimensionally-equivalent types may be representing different quantities, so this would require casting the result to either torque or energy, else you will just get the 'anonymous-quantity' output in the above example. But it was .. a cool trick :-)
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.
cool.... but complicated ...
To sidestep this "implicit"/stream-stored formatting, it might be better
use casting, as you use in the library, provided that you can then print
to 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
Well if you still have the code .... OTOH as I indicated previously .. The SI rules are pretty rigid, and there is also an argument for making one basic standard output 'signature' by which to identify a particular type for char stream anyway. I do like simplicity .. At the end of the day this is an important element of the physical-quantity type.Its pretty humble. #include the headers, declare them in your code and get on with the real app. They are just little building blocks... like std::string or inbuilts. the
degrees are different.
I would have to implement these as separate ("seamlessly integrated") types, theyre real oddities they are :-) ... todo
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
As you say its all those details ... like centigrade, kg, etc ;-) them
to
you.
Thanks ... received ..
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.
Thanks for testing this out.
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
Fair comment.. I'll try to get it working 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. :)
Ok Thanks for the time you have spent on this. What I take from this overall is that the docs need a major redo. (And some historical background/ refs). As it stands I think the docs are often saying the same thing in 3 places... none of which is the right place. Perhaps I need a bit more of a top down approach for a change. regards Andy Little