Boost logo

Boost :

From: Andrey Semashev (andysem_at_[hidden])
Date: 2007-01-16 16:04:05


Hello Matthias,

Tuesday, January 16, 2007, 7:23:44 AM, you wrote:

[snip]

>> And construction with no explicit casting but via explicit constructor
>> should be possible too:
>>
>> quantity< double, SI::length > q1(1.5);
>> quantity< int, SI::length > q2(100);

> My paradigm in designing this library is to be able to 1) prevent the
> Mars Climate Orbiter disasters and
> 2) to facilitate changing of unit systems in code with maximal
> safety. My concern with having an explicit
> constructor with only a value_type argument is that someone who
> decided to change from SI to CGS systems
> could easily forget to update the constructor arguments :

> quantity<double,SI::length> q1(1.0); // 1 meter

> would get changed to

> quantity<double,CGS::length> q1(1.0); // 1 centimeter

> with no indication that there was any problem. If we allow explicit
> unit system conversion, this change would
> behave as expected:

> quantity<double,SI::length> q1(1.0*meter); // 1 meter

> becomes

> quantity<double,CGS::length> q1(1.0*meter); // 100 centimeters

> and, therefore, be safe. The same is true if we require quantity_cast
> for unit system conversion.

Well, maybe you're right here.

>> And this IMO should go implicitly:
>>
>> // Conversion from int to double via assignment
>> q1 = q2;

> I'm not a big lover of implicit type conversion. This is again an
> issue of maximizing the safety of using quantities. If we allow
> implicit value_type conversions, we permit truncation in cases of
things like double->>int. Preventing this does add one more layer of
> safety netting. On the other hand, there is something to be said for
> having quantities simply delegate their value_type conversions to the
> value_type itself - I guess I'm happy to go either way, depending on
> the consensus...

IMHO, such implicit conversions would be more natural for users (for
me at least). Of course, such conversions should be valid only if
these representation types are implicitly convertible (a user may
ensure they are not, if it's necessary). And if there is a percision
loss on such conversion, a well-mannered compiler will issue a warning.

>> And quantity conversion should require explicit casting:
>>
>> quantity< double, SI::centigrade > q4;
>> quantity< double, SI::kelvin > q5;
>> q5 = quantity_cast< SI::kelvin >(q4);

> Of course, the centigrade<->kelvin issue opens a new can of worms
> since that conversion is affine, not linear.

My humble knowledge may have confused me but I thought that the same
temperature by Centigrade and by Kelvin will always differ by about
273 (i.e. the conversion is linear). Which is not right in case of
Farenheit.

> At present,
> this conversion is not implemented in the library. My preference
> would be to define a value_type that models a unit with offset that
> could be converted to linear value_types... Right now, the library
> allows implicit unit system conversions (as described above), and,
> with quantity_cast, would allow casting as well.

Maybe the library should offer an opportunity to extend the out of box
set of supported quantities. If you agree with me here, there should
be a way of specifying user-defined quantities and conversion rules
between them. I see it something like this:

template< typename FromT, typename ToT >
struct conversion_rule;

template< >
struct conversion_rule<
  unit< SI::kelvin, SI::grade >,
  unit< SI::centigrade, SI::grade >
>
{
  template< typename T >
  static T apply(T const& value)
  {
    return value - 273;
  }
};

template< >
struct conversion_rule<
  unit< SI::centigrade, SI::grade >,
  unit< SI::kelvin, SI::grade >
>
{
  template< typename T >
  static T apply(T const& value)
  {
    return value + 273;
  }
};

template< class System2, class System1, class Dim, class Y >
quantity< Y, unit< System2, Dim > >
quantity_cast(const quantity< Y, unit< System1, Dim > >& from)
{
  typedef conversion_rule<
    unit< System1, Dim >, unit< System2, Dim >
> conversion_rule_t;
  return conversion_rule::apply(from.get()); // here acquire the
  // actual value of the quantity, it'll be of type Y
}

Although the structures as the conversion rules is not the best way to
implement it (making use of free functions for this purpose would be
more flexible because of ADL involvement), the main idea is to extract
the conversion algorithm to a user-defined entity.

>> PS: And another couple of cents. Maybe the representation type of
>> "quantity" template should be optional defaulted to double or, the
>> better way, to the type that is natural for the quantity type. I.e.,
>> for "SI::length" the "double" would be natural.

> Paul Bristow has brought this up, too. I really, really wish there
> was a template typedef facility. Lacking that, I'm inclined to
> define, in the boost::units::SI namespace, another quantity class
> that defaults to the SI system and double precision value_type. This
> could invert the order of template arguments, too:

> namespace SI {

> template<class Unit,class Y = double> class quantity : public
> boost::units::quantity<Y,Unit> { ... };

> }

As it was mentioned by Peder Holt, it is quite possible to make these
template parameters irrelevant of the position. I'd do something like
that:

struct unit_base {};

template< typename T1, typename T2 >
struct unit : public unit_base
{
};

template< typename T >
struct is_unit : public is_base_and_derived< T, unit_base > {};

// Double bool check is required to detect erroneous quantity
// instantiations
template<
  typename T1,
  typename T2,
  bool = is_unit< T1 >::value,
  bool = is_unit< T2 >::value
>
struct quantity_tmplt_params;

template< typename T1, typename T2 >
struct quantity_tmplt_params< T1, T2, true, false >
{
  typedef T1 unit_type;
  typedef typename mpl::if_<
    is_same< T2, void >,
    typename unit_type::default_value_type,
    T2
>::type value_type;
};

template< typename T1, typename T2 >
struct quantity_tmplt_params< T1, T2, false, true >
{
  typedef T2 unit_type;
  typedef typename mpl::if_<
    is_same< T1, void >,
    typename unit_type::default_value_type,
    T1
>::type value_type;
};

// It's better to use void for defaults as it shortens mangled names
template< typename T1, typename T2 = void >
class quantity :
  public quantity_impl<
    typename quantity_tmplt_params< T1, T2 >::value_type,
    typename quantity_tmplt_params< T1, T2 >::unit_type
>
{
};

-- 
Best regards,
 Andrey                            mailto:andysem_at_[hidden]

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