Boost logo

Boost :

From: Andy Little (andy_at_[hidden])
Date: 2006-07-02 18:31:45


"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


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