Boost logo

Boost :

From: Andy Little (andy_at_[hidden])
Date: 2004-10-24 19:28:20


> "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.

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.

> 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.

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.

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.

> 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?

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.

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.

> 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...

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?

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.

> 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).

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.

> 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. :)

 ;-) 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
to
> use casting, as you use in the library, provided that you can then print
the
> resulting quantity any way you want.

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.

> 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.

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. ;) )

As you say its all those details ... like centigrade, kg, etc ;-)

> > > 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.

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).

Fair comment.. I'll try to get it working

> > > 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. :)

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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk