
Steven,
As far as putting the system into dim is concerned, I think this is an excellent idea. If you store the dimension in the low order bits and the system in the high order bits, compilation time should be unaffected if you are only working in a single system. For more complex cases
It was actually much easier to implement the system inside dim<> than I thought it would be; other than menial changes, all I had to do to get things working is redefine the comparator : template<typename S,typename T,typename V> struct dim { typedef S system_type; typedef T tag_type; typedef V value_type; }; /// less than comparison for unit tag sorting template<typename T,typename V> struct less; template<typename S1,typename T1,typename V1, typename S2,typename T2,typename V2> struct less< dim<S1,T1,V1>,dim<S2,T2,V2> > { typedef typename mpl::less<typename S1::value, typename S2::value>::type system_less_type; typedef typename mpl::less<typename T1::value, typename T2::value>::type tag_less_type; typedef typename mpl::if_<mpl::equal_to<S1,S2>, tag_less_type, system_less_type>::type type; }; Then, if you enumerate the systems: namespace SI { struct system : public ordinal<1> { }; } namespace CGS { struct system : public ordinal<2> { }; } everything works so far. Still need to implement the operators and unit conversion, which will involve a bit more work, but this actually looks feasible. All the example programs except those doing unit conversion already compile and run. We'll see if modifying the operators adds too much overhead at compile-time... Matthias

AMDG Matthias Schabel <boost <at> schabel-family.org> writes:
everything works so far. Still need to implement the operators and unit conversion, which will involve a bit more work, but this actually looks feasible. All the example programs except those doing unit conversion already compile and run. We'll see if modifying the operators adds too much overhead at compile-time...
Matthias
I apologize. The comparisons in dimension.hpp are currently hard coded to use operator<. You'll need to fix sort and merge. In Christ, Steven Watanabe

I apologize. The comparisons in dimension.hpp are currently hard coded to use operator<. You'll need to fix sort and merge.
No need to apologize - I appreciate the effort you put into optimization. This will be a bit more complicated, unfortunately. The code for the merge was relatively straightforward to add: template<int N1, int N2> struct merge_dimensions_impl { template<typename Begin1, typename Begin2> struct apply { typedef typename boost::mpl::deref<Begin1>::type dim1; typedef typename boost::mpl::deref<Begin2>::type dim2; typedef typename merge_dimensions_func< (less<dim1,dim2>::value == true), (boost::is_same<dim1,dim2>::value == true)>::template apply< Begin1, Begin2, N1, N2 >::type type; }; }; but in the partition you only pass the tag value, so I can't just drop less<> in... I'll need to pass the whole dim<>, then insert using less<>... template<int N> struct partition_dims_forward_impl { template<typename Begin, typename State, long Value> struct apply { typedef typename Begin::item::tag_type::value val; typedef typename partition_dims_forward_impl<N - 1>::template apply< typename Begin::next, typename partition_dims_state_insert<((val::value) < Value)>::template apply<State, typename Begin::item>::type, Value >::type type; }; }; In addition, modifying the merge appears to cause problems with dimensionless units...sigh. On a positive note, I think multiplication/division of units should just work once the sort works correctly. Unit conversion will be a little more complicated; I think it will work with two constraints: 1) The target unit may only have one instance of each tag (no repeated tags with different systems). Otherwise, the conversion becomes ambiguous. 2) The source unit must have exactly the same set of tags as the target unit. To do conversion, we'll have to pull out a list of all list elements having a given tag, then convert the ones not in the same system as the same tag in the target unit, then merge. Anyway, this is probably not interesting to anyone...don't mind my blathering... Matthias

AMDG Matthias Schabel <boost <at> schabel-family.org> writes:
1) The target unit may only have one instance of each tag (no repeated tags with different systems). Otherwise, the conversion becomes ambiguous.
If the source has only one instance of each tag than it should be easy too.
To do conversion, we'll have to pull out a list of all list elements having a given tag, then convert the ones not in the same system as the same tag in the target unit, then merge.
Anyway, this is probably not interesting to anyone...don't mind my blathering...
Matthias
A more sophisticated data structure might be appropriate dim< length_tag, mpl::list< mpl::pair<SI::system, static_rational<2> >, mpl::pair<CGS::system, static_rational<1> > >
In Christ, Steven Watanabe

1) The target unit may only have one instance of each tag (no repeated tags with different systems). Otherwise, the conversion becomes ambiguous.
If the source has only one instance of each tag than it should be easy too.
But in general we should assume that constructs like this are legit: quantity<SI::volume> V(1.0*SI::meters*imperial::feet*CGS::centimeters); where the system of the result type defines the conversions imperial::feet->SI::meters CGS::centimeters->SI::meters to get the conversion factor...this is unambiguous in all cases, AFAICS.
A more sophisticated data structure might be appropriate
dim< length_tag, mpl::list< mpl::pair<SI::system, static_rational<2> >, mpl::pair<CGS::system, static_rational<1> >
I guess the problem is that the optimal data structure for compile-time algebra won't be the same as the optimal data structure for conversions. For the former, you really just have three steps: 1) append two sequences 2) sort by unit system/tag type 3) reduce duplicate tags For the latter, you really want to 1) sort source type by tag_type 2) iterate over systems, converting to destination system 3) reduce duplicate tags Seems like these two are at odds, but I may just not be thinking about it right. Also, making the data structure more complicated would, I imagine, pose a problem for compile times again... Matthias

AMDG Matthias Schabel <boost <at> schabel-family.org> writes:
1) The target unit may only have one instance of each tag (no repeated tags with different systems). Otherwise, the conversion becomes ambiguous.
If the source has only one instance of each tag than it should be easy too.
But in general we should assume that constructs like this are legit:
quantity<SI::volume> V(1.0*SI::meters*imperial::feet*CGS::centimeters);
where the system of the result type defines the conversions
imperial::feet->SI::meters CGS::centimeters->SI::meters
to get the conversion factor...this is unambiguous in all cases, AFAICS.
I didn't mean that it shouldn't be valid. I meant that both should be allowed. Otherwise, conversions are not necessarily reversible. In Christ, Steven Watanabe

Matthias Schabel <boost <at> schabel-family.org> writes:
But in general we should assume that constructs like this are legit:
quantity<SI::volume> V(1.0*SI::meters*imperial::feet*CGS::centimeters);
where the system of the result type defines the conversions
imperial::feet->SI::meters CGS::centimeters->SI::meters
to get the conversion factor...this is unambiguous in all cases, AFAICS.
This is possible, but is it a good idea? In C++ expressions are context-independent; that is, x * y * z * w always has a specific type T, regardless of whether it's assigned to an int, used to construct a quantity<SI::volume>, passed to a function taking double, or passed to a function template taking X const& x, where X is a template parameter. This has the advantage that it scales well. Consider ( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0 * imperial::feet ) and other complex expressions. Pushing down a result type may be hard. If your result type is nautical::miles * imperial::miles, how do you decide which of the two branches needs to get the nautical::miles? And this is a relatively simple example.

( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0 * imperial::feet )
I'm not proposing to allow heterogeneous addition where, as you point out, the result type is ambiguous. Multiplication/division don't have this problem, though. Your example would give 2.0 m cm + 1.0 m ft + 2.0 ft cm + 1.0 ft^2 which wouldn't be allowed due to the addition of different units. Any quantity h as a dimensional signature that consists (limiting the discussion to physical units for simplicity) of the powers of the various dimensions. That is (calling length L, time T, and mass M) L^2 M^-3 T is a dimensional signature that could be represented all in SI: m^2 kg^-3 s or mixed SI and CGS m cm kg^-1 g^-2 s m cm kg^-2 g s etc... As long as you can come up with an invertible one-to-one map between different heterogeneous units with identical dimensional signatures, this is OK.
and other complex expressions. Pushing down a result type may be hard. If your result type is nautical::miles * imperial::miles, how do you decide which of the two branches needs to get the nautical::miles? And this is a relatively simple example.
Another issue, as Steven pointed out, is that it is harder to get a nice syntax in the case where you want a function that takes a dimension signature but doesn't care about the system (or systems) of units. That is, the current implementation, which requires that all fundamental units belong to one system, lets you do this: template<class System> quantity<unit<System,energy_type> > work(const quantity<unit<System,force_type> >& F, const quantity<unit<System,length_type> >& dx) { return F*dx; } or even template<class System1,class System2,class System3> quantity<unit<System3,energy_type> > work(const quantity<unit<System1,force_type> >& F, const quantity<unit<System2,length_type> >& dx) { const quantity<unit<System3,force_type> > Fc(F); const quantity<unit<System3,length_type> > dxc(dx); return Fc*dxc; } when you allow units to be heterogeneous across systems, it becomes difficult to implement this...suggestions are welcome... Maybe this is too much of a hairball to try to deal with. Matthias

Peter Dimov wrote:
Matthias Schabel <boost <at> schabel-family.org> writes:
But in general we should assume that constructs like this are legit:
quantity<SI::volume> V(1.0*SI::meters*imperial::feet*CGS::centimeters);
where the system of the result type defines the conversions
imperial::feet->SI::meters CGS::centimeters->SI::meters
to get the conversion factor...this is unambiguous in all cases, AFAICS.
This is possible, but is it a good idea? In C++ expressions are context-independent; that is, x * y * z * w always has a specific type T, regardless of whether it's assigned to an int, used to construct a quantity<SI::volume>, passed to a function taking double, or passed to a function template taking X const& x, where X is a template parameter. This has the advantage that it scales well. Consider
( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0 * imperial::feet )
and other complex expressions. Pushing down a result type may be hard. If your result type is nautical::miles * imperial::miles, how do you decide which of the two branches needs to get the nautical::miles? And this is a relatively simple example.
<Warning. This is a possibly totally misuided rant> Yuck. Please forgive me for stepping into the middle of this discussion without having read any of the previous discussion carefully (I have been scanning all of the messages as they were posted), but the fact that this discussion is even taking place makes me feel like everyone is going down the wrong path. Does anyone really have software with code like ( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0 * imperial::feet ) or std::cout << x + y << '\n'; (where x and y have the same dimension but different units) Does anyone really want a library that makes this possible? I'm far from an expert in software engineering. Nor do I know exactly what others are using the dimension/units library for. But the last thing I want is a library that allows code like this. Let me try to make some points (which I concede are totally self-centered): 1) To me, the purpose of such a library is *not* to be able to mix different units of the same dimension inside a single expression. I'm sorry, but I feel that the existence of an arithmetic expression with different units for the same dimension is a clear sign of poor software design. I would certainly redesign my code to eliminate anything like this. There are just too many possible pitfalls. Since I can't rule out the possibility of needing a mixed-units expression, I would simply allow it only through explicit unit-conversion functions. 2) For me, one purpose of a dimensions/units library is *code reuse*. It allows me to implement algorithms generically (using templates) without making any assumptions of what the units used for each dimension are, except to say that they are consistent with each other (so if distance is in miles and time is in seconds, then speed must be in miles per second). I can then use the same code in different settings that use different sets of units and even different sets of dimensions(!). 3) Another purpose is compile-time checking that all of the formulas in my algorithm are logically consistent regarding dimensions/units. Again, this requires no conversion between different units for the same dimension. 4) Another thing I like to do is implementing a generic interface for a code module. So even if within the code module I have locked in the units used for all the computations within the module, I want to be able to pass in the input values in any units and not just the ones actually used in the implementation. So what I do is I implement templated constructors or functions and the first thing the function/constructor does is convert the input value into the value using the internal units. For a constructor, this can be done in the initialization list. The dimensions/units types make this very easy to detect at compile-time what the units of the input value are and performing the appropriate conversion. So my code often looks something like this: class ComputeObject { typedef meters internal_distance_type; typedef kilograms internal_mass_type; template <class DistanceType, class MassType> ComputeObject(DistanceType distance, MassType mass) :distance_(unit_cast<internal_distance_type>(distance)), mass_(unit_cast<internal_mass_type>(mass)) { ...(do computations using internal units)... } // Output functions template <class MassType> MassType some_calculated_mass() const { ...(do calculations using internal types)... return unit_cast<MassType>(answer); } }; 5) If I need runtime chosen units (and assuming that the conversion factors have been locked in at compile-time), then this is rather easy to implement as a layer above the interface described in 4). But it does require using dynamic types that have nothing to do with the dimension/units library, in which units are set at compile-time. 6) In practice, I now write all my mathematical algorithms in templated classes and functions that make no assumptions about what the dimensions and units are. Only when I write library and application interfaces do I "lock" in what the units and dimensions are. All of the code dealing with specific units or dimensions are in the interface code only. (Is it possible that this is where I, a mathematician, differ from the physicists? Physicists feel that various formulas, though valid for different units, are still tied to specific dimensions. I don't. The same formula can be useful in different settings, where the dimensions involved are completely different. The heat equation and its solution is a spectacular example of this; it is certainly important in physics, but it is equally important in finance but using different dimensions. So I would not want my code to lock in what the dimensions are, never mind the units. The irony is that this means I want a units library and the physicists seem to prefer a dimensions library.) So this means that for *me* and, I guess, *me* only: 1) I do not need a library that insists on defining some "system of units" to which everything gets converted. 2) I am strongly against implicit conversion of units. If any line of my code has something in the wrong set of units, it means to me I screwed up the interface of the function that line of code is in. For me, units conversion *only* occurs at the interface of a function or constructor and never anywhere after that. <end rant> At best, it appears to me that how I use the dimension/unit library is very different from what everybody is doing. So I will retire from the discussion for now. If I have missed the point entirely, I apologize and please feel free to pretend my post never existed. Some day (probably long after I have retired), I'll try to make my library fit for public viewing and post it.

On 2/16/07, Deane Yang <deane_yang@yahoo.com> wrote:
Yuck. Please forgive me for stepping into the middle of this discussion without having read any of the previous discussion carefully (I have been scanning all of the messages as they were posted), but the fact that this discussion is even taking place makes me feel like everyone is going down the wrong path.
Does anyone really have software with code like
( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0 * imperial::feet )
Not exactly like that no, but I can say that I have software that definitely deals with mixed units. Units that do not come from code, but come from a database. Changing those databases would break all of the software that uses them, so that's not an option. As a quick example, here are some of the types of data plus their units: Terrain elevation : meters Aircraft altitude : feet Radar range : nautical miles Radar max ceiling : feet Radar beam angle : degrees (I only listed this because mcs::units includes it) Atmospheric Refractivity Index : unitless
Does anyone really want a library that makes this possible?
I'm far from an expert in software engineering. Nor do I know exactly what others are using the dimension/units library for. But the last thing I want is a library that allows code like this.
Let me try to make some points (which I concede are totally self-centered):
1) To me, the purpose of such a library is *not* to be able to mix different units of the same dimension inside a single expression. I'm sorry, but I feel that the existence of an arithmetic expression with different units for the same dimension is a clear sign of poor software design. I would certainly redesign my code to eliminate anything like this. There are just too many possible pitfalls.
One example I can think of is getting the height of a radar beam, given a range and a beam angle. The common one is (I've factored out the beam angle, assume it's 0): h = R^2 / 2ka where h is the height, R is the range, k is the refractivity (commonly the 4/3 Earth model), and a is the Earth's radius. That formula assumes that R and a are both in the same unit system, and consequently h will be given in that unit. Now in my case, R would be given in nautical miles, Earth's radius would be given in meters, and I would want h to be in feet so that I could compare it against an aircraft's altitude. For me to use that formula, I would need unit conversions somewhere, presumably immediately after extraction from the database, and then all code henceforth would use one unit system. But there exists a formula that approximates the same thing, only it takes in R as nautical miles, and returns h in feet? h = (R / 1.23)^2 where R is in nautical miles, and h is returned in feet. That formula is much better for me, since all of the radar ranges are given in nautical miles, and all of the aircraft altitudes are given in feet. The difference between the two is usually under 80 feet. Across unit system operations/comparisons seems really neat to me, but I'm unsure of the ramifications. Surely you are no longer in the compile-time only world? I'm happy enough with what Matthias has done so far, but supporting that would be really cool I think. E.g. for me, it would be really nice to compare the aircraft's height to the ground elevation (to make sure we're not flying into a mountain) without doing a unit conversion first, since aircraft altitude is always given in feet, and ground elevation in meters. This is definitely run-time support though. I think the difference is that the current system will guarantee safety by not compiling the following, while the run-time units would guarantee safety by allowing it to compile and doing the correct conversions behind the scenes: // maintain a certain altitude above ground level if (aircraft.altitude > ground_elevation + min_agl_offset) { // ... } else { // ... } I see both as enormously useful, the compile-time one as required, and the run-time support as optional. --Michael Fawcett

Michael Fawcett wrote:
On 2/16/07, Deane Yang <deane_yang@yahoo.com> wrote:
Yuck. Please forgive me for stepping into the middle of this discussion without having read any of the previous discussion carefully (I have been scanning all of the messages as they were posted), but the fact that this discussion is even taking place makes me feel like everyone is going down the wrong path.
Does anyone really have software with code like
( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0 * imperial::feet )
Not exactly like that no, but I can say that I have software that definitely deals with mixed units. Units that do not come from code, but come from a database. Changing those databases would break all of the software that uses them, so that's not an option. As a quick example, here are some of the types of data plus their units:
Terrain elevation : meters Aircraft altitude : feet Radar range : nautical miles Radar max ceiling : feet Radar beam angle : degrees (I only listed this because mcs::units includes it) Atmospheric Refractivity Index : unitless
<snip>
One example I can think of is getting the height of a radar beam, given a range and a beam angle. The common one is (I've factored out the beam angle, assume it's 0):
h = R^2 / 2ka
where h is the height, R is the range, k is the refractivity (commonly the 4/3 Earth model), and a is the Earth's radius.
That formula assumes that R and a are both in the same unit system, and consequently h will be given in that unit.
Now in my case, R would be given in nautical miles, Earth's radius would be given in meters, and I would want h to be in feet so that I could compare it against an aircraft's altitude. For me to use that formula, I would need unit conversions somewhere, presumably immediately after extraction from the database, and then all code henceforth would use one unit system.
But there exists a formula that approximates the same thing, only it takes in R as nautical miles, and returns h in feet?
h = (R / 1.23)^2
where R is in nautical miles, and h is returned in feet. That formula is much better for me, since all of the radar ranges are given in nautical miles, and all of the aircraft altitudes are given in feet. The difference between the two is usually under 80 feet.
<snip>
// maintain a certain altitude above ground level if (aircraft.altitude > ground_elevation + min_agl_offset) { // ... } else { // ... }
Ah, the dumb mathematician (me) finally gets it. The thing I never saw before is "aircraft.altitude". The point is that the different quantities (of the same dimension) come from member data of different objects, so they're all in different units. And now you want to compare or add them together. I'd still use explicit casts to put everything into the same set of units (I like to be able to see explicitly what's going on), but I definitely have to concede that a reasonable desire is to have the conversions be done automatically and behind the scenes. I would still prefer to write something like if (aircraft.altitude > ground_elevation + min_agl_offset) as if (unit_cast<elevation_type>(aircraft.altitude) > ground_elevation + min_agl_offset) So I know and can see easily that what units the computation takes place in. If you have a clever dimensions library that handles everything without explicit casting, then you don't know what units the computation uses. Admittedly, this is probably a misplaced worry of mine, but I can certainly envision some kind of iterative computation, where mixing the wrong units and having the library make a bad (unseen) choice of units to do the computation leads to invalid results. I have a strong aversion to hidden choices and effects like this.

if (aircraft.altitude > ground_elevation + min_agl_offset)
as
if (unit_cast<elevation_type>(aircraft.altitude) > ground_elevation + min_agl_offset)
This is actually a very good example of the problem; here there are two steps, the addition operation and the comparison. Without specifying a priori a particular common base unit system through which all conversions are mediated (which is basically the same as requiring everyone use one unit system), this is just fundamentally ambiguous - it has nothing to do with implementation. If I ask : Is 3 feet plus 1 meter less than one fathom? How would you answer it? Naturally, you would want to convert to some common unit of length, but which one? There are three choices, and all are roughly equivalent. Now, in this case, it doesn't matter since all three systems would give you the same answer, but since there is no good way for the compiler to make this decision automatically,the reasonable thing for it to do is issue an error. this problem doesn't go away if you have runtime units either - it is just fundamentally bad practice to have the compiler make arbitrary decisions in arithmetic expressions. Matthias

On 2/16/07, Matthias Schabel <boost@schabel-family.org> wrote:
if (aircraft.altitude > ground_elevation + min_agl_offset)
as
if (unit_cast<elevation_type>(aircraft.altitude) > ground_elevation + min_agl_offset)
This is actually a very good example of the problem; here there are two steps, the addition operation and the comparison. Without specifying a priori a particular common base unit system through which all conversions are mediated (which is basically the same as requiring everyone use one unit system), this is just fundamentally ambiguous - it has nothing to do with implementation. If I ask :
Is 3 feet plus 1 meter less than one fathom?
How would you answer it? Naturally, you would want to convert to some common unit of length, but which one? There are three choices, and all are roughly equivalent. Now, in this case, it doesn't matter since all three systems would give you the same answer, but since there is no good way for the compiler to make this decision automatically,the reasonable thing for it to do is issue an error. this problem doesn't go away if you have runtime units either - it is just fundamentally bad practice to have the compiler make arbitrary decisions in arithmetic expressions.
I see. I find this area interesting, although I am hoping you stick to compile-time only for now. I think the only hard part is the arithmetic portion. A comparison only deals with two types, and it doesn't matter which way (precision aside, maybe there could be some smarts here) the conversion happens, the result is the same. 3 feet + 1 meter, what is the result? What are the pros/cons to always using the left-hand-side type? If a decision was made, arithmetic would be defined, and comparisons would be easy. I realize this should not be a focus of the current library, and again, I'm not asking for it. I do not know what the ramifications in terms of implementation and usage are. It just interested me. --Michael Fawcett

On 2/16/07, Deane Yang <deane_yang@yahoo.com> wrote:
Ah, the dumb mathematician (me) finally gets it. The thing I never saw before is "aircraft.altitude". The point is that the different quantities (of the same dimension) come from member data of different objects, so they're all in different units. And now you want to compare or add them together.
I'd still use explicit casts to put everything into the same set of units (I like to be able to see explicitly what's going on), but I definitely have to concede that a reasonable desire is to have the conversions be done automatically and behind the scenes.
I would still prefer to write something like
if (aircraft.altitude > ground_elevation + min_agl_offset)
as
if (unit_cast<elevation_type>(aircraft.altitude) > ground_elevation + min_agl_offset)
Same here, I was only wondering aloud. In fact I hope Matthias will *only* provide the compile-time version in this first iteration.
So I know and can see easily that what units the computation takes place in. If you have a clever dimensions library that handles everything without explicit casting, then you don't know what units the computation uses. Admittedly, this is probably a misplaced worry of mine, but I can certainly envision some kind of iterative computation, where mixing the wrong units and having the library make a bad (unseen) choice of units to do the computation leads to invalid results. I have a strong aversion to hidden choices and effects like this.
Well, I have a question to Matthias concerning similar things. How does one use a unitless number? Do you use dimensionless? I posted two formulas that are from a radar handbook. The first is: h = R^2 / 2ka where h is the beam height, R is radar range, k is the refractivity (usually 4/3), and a is the Earth's radius. R and a must be the same unit, and h is returned in that unit. How would you code this using mcs::units? // Is this correct? How would I know what system's dimensionless to use? template <typename T> T radar_beam_height(T range, T earth_radius, quantity<SomeSystem::dimensionless> refractivity = quantity<SomeSystem::dimensionless>(4/3)) { const quantity<SomeSystem::dimensionless> two(2); return quantity<T>( pow(range, 2) / ( two * refractivity * range ) ); } With calling code like: // What's the radar beam height at 300 nautical miles using the mean Earth radius? quantity<SI::length> height = radar_beam_height(quantity<SI::length>(1028971200), quantity<SI::length>(6371008.7714)); The other question I have is very open-ended. An approximation for the above formula where R is given in nautical miles, and h is returned as feet is: h = (R / 1.23)^2 How would you go about implementing that with mcs::units? I have no idea what unit 1.23 is, or how they even derived that number, other than it has the Earth radius and conversion from nautical miles to feet built-in. quantity<imperial::feet> radar_beam_height(quantity<nautical::miles> range) { // This is probably not actually dimensionless, it is probably something like // nautical miles / (nautical miles * sqrt(ft)), but that seems meaningless const quantity<SomeSystem::dimensionless> c(1.23); using namespace std; return quantity<imperial::feet>(pow(range / c, 2) ); } Many thanks, --Mike

Hi Michael,
Same here, I was only wondering aloud. In fact I hope Matthias will *only* provide the compile-time version in this first iteration.
So do I ;^) Thanks for the support.
Well, I have a question to Matthias concerning similar things. How does one use a unitless number? Do you use dimensionless?
There is a dimensionless tag, but quantities of dimensionless units are specialized so that they behave just like regular value_types - by definition if something is dimensionless, there is no difference in its value, irrespective of unit system. It is mainly there to allow checking for results of dimensional calculations - if you get a dimensionless unit, it came from a dimensional analysis calculation. But there is no real benefit to explicitly constructing dimensionless quantities in general; for unit computations (without associated values) it can be nice to be able to output explicitly that the result of some calculation is dimensionless.
I posted two formulas that are from a radar handbook. The first is:
h = R^2 / 2ka
where h is the beam height, R is radar range, k is the refractivity (usually 4/3), and a is the Earth's radius. R and a must be the same unit, and h is returned in that unit. How would you code this using mcs::units?
For lengths in meters, this would be : template<typename T> quantity<SI::length,T> radar_beam_height(const quantity<SI::length,T>& radar_range, const quantity<SI::length,T>& earth_radius, T k = 4.0/3.0) { return quantity<SI::length,T>(pow<2>(radar_range)/ (2.0*k*earth_radius)); } To be more general, we can specialize for arbitrary unit system: template<class System,typename T> quantity<unit<System,length_type>,T> radar_beam_height(const quantity<unit<System,length_type>,T>& radar_range, const quantity<unit<System,length_type>,T>& earth_radius, T k = 4.0/3.0) { return quantity<unit<System,length_type>,T>(pow<2>(radar_range)/ (2.0*k*earth_radius)); } This function works as long as all quantities are of the same system. You could further generalize it to work with three different types of lengths : one for the radar range, one for the earth radius, and one for the beam height: template<class return_type,class System1,class System2,typename T> return_type radar_beam_height(const quantity<unit<System1,length_type>,T>& radar_range, const quantity<unit<System2,length_type>,T>& earth_radius, T k = 4.0/3.0) { // need to decide which system to use for calculation const return_type rr(radar_range), er(earth_radius); return return_type (pow<2>(rr)/(2.0*k*er)); } This way, if your code was like this: quantity<nautical::length> radar_range(24.5*miles); quantity<SI::length> earth_radius(6371e3*meters); you could do this: quantity<CGS::length> beam_height = radar_beam_height<CGS::system> (radar_range,earth_radius); and get the value computed in centimeters.
template <typename T> T radar_beam_height(T range, T earth_radius,
I would return a quantity of length and pass arguments of length to retain type safety
quantity<SomeSystem::dimensionless> refractivity = quantity<SomeSystem::dimensionless>(4/3))
No need, dimensionless is equivalent to T. Watch out for integer division in 4/3...
const quantity<SomeSystem::dimensionless> two(2);
Again, just a scalar T is fine here.
return quantity<T>( pow(range, 2) / ( two * refractivity * range ) );
You need to use the static power function since powers/roots change the dimensions of the units, and a return quantity in the units you like: return quantity<SI::length>(pow<2>(range)/(two*refractivity*range));
With calling code like: // What's the radar beam height at 300 nautical miles using the mean Earth radius? quantity<SI::length> height = radar_beam_height(quantity<SI::length>(1028971200), quantity<SI::length>(6371008.7714));
You can make this simpler: quantity<SI::length> height = radar_beam_height(quantity<SI::length>(quantity<nautical::length> (300.0)), quantity<SI::length>(6371.0087714*kilo*meters));
The other question I have is very open-ended. An approximation for the above formula where R is given in nautical miles, and h is returned as feet is:
h = (R / 1.23)^2
How would you go about implementing that with mcs::units? I have no idea what unit 1.23 is, or how they even derived that number, other than it has the Earth radius and conversion from nautical miles to feet built-in.
Engineering approximations can be problematic in the sense that they are often completely empirical and, therefore, don't particularly respect the rules of dimensional analysis. In these cases it's best to just wrap the approximation in a function that takes the correct arguments and returns the appropriate value: quantity<imperial::length> radar_beam_height(const quantity<nautical::length>& range) { return quantity<imperial::length>(std::pow(range.value()/1.23,2.0) *imperial::feet); } If you really wanted to think about the units of your approximation in great detail, you have : feet = (nautical miles/(something))^2 so something = nautical miles/feet^(1/2) and your constant is C = 1.23 nm/ft^1/2 This is actually a good example of where heterogeneous units would be useful... Anyway, check out the attached example code for functional versions of the above. You will also need to patch conversion.hpp - there is a minor bug in it that I needed to fix.
quantity<imperial::feet> radar_beam_height (quantity<nautical::miles> range)
It's important to maintain a semantic distinction in this library : by convention, I have the template arguments refer to the type of unit (length, mass, etc...) rather than the specific unit (meters, kilograms, etc...)... An important proviso, though : because you cannot use floating point numbers as template arguments, there will be some engineering approximations that cannot be expressed in dimensionally correct form (those with floating point powers and/or roots). However, those formulas are best wrapped in a dimensionally correct wrapper function in any case, to guarantee that the intended unit is correct. Matthias 

Does anyone really have software with code like
( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0 * imperial::feet )
This was a misinterpretation of what I was suggesting. The problem I'm trying to deal with is that of things like kilowatt hours flops / dollar m cm in Right now mcs::units provides two options: 1) define unit quantities for the odd units in terms of units in some chosen unit system: static const quantity<SI::power> kilowatt = 1000.0*SI::watt; static const quantity<SI::time> hours = 3600.0*SI::seconds; and 2) define a new unit system that contains all the units that you want to mix together The problem with 1) is that you are converting, which incurs added computation and, potentially, precision loss. The problem with 2) is that it involves more coding and also that you can't easily support cases where you have different units of the same physical quantity such as volumes in meters*centimeters*inches. The simplification would be for users of mixed systems, who would only have to have the appropriate systems defined once somewhere and could then freely mix and match units. My feeling at the moment is that getting this to work may add too much additional complexity and compile- time overhead for what is essentially an edge case. It's still an intriguing challenge, though...
So my code often looks something like this:
class ComputeObject { typedef meters internal_distance_type; typedef kilograms internal_mass_type;
template <class DistanceType, class MassType> ComputeObject(DistanceType distance, MassType mass) :distance_(unit_cast<internal_distance_type>(distance)), mass_(unit_cast<internal_mass_type>(mass)) { ...(do computations using internal units)... }
// Output functions
template <class MassType> MassType some_calculated_mass() const { ...(do calculations using internal types)...
return unit_cast<MassType>(answer); } };
This is how you would write it in mcs::units: class ComputeObject { typedef quantity<SI::length> internal_distance_type; typedef quantity<SI::mass> internal_mass_type; template<class System> ComputeObject(quantity<unit<System,length_type> > distance, quantity<unit<System,mass_type> > mass) : distance_(distance), mass_(mass) { ... } template<class System> quantity<unit<System,mass_type> > some_calculated_mass() const { ... return quantity<unit<System,mass_type> >(answer); } };
1) I do not need a library that insists on defining some "system of units" to which everything gets converted.
A major design goal of mcs::units was to allow users to work in a system that matches their problem. These can be physical units, but can be anything else, also...
2) I am strongly against implicit conversion of units. If any line of my code has something in the wrong set of units, it means to me I screwed up the interface of the function that line of code is in. For me, units conversion *only* occurs at the interface of a function or constructor and never anywhere after that.
There is a small but vocal camp in favor of implicit unit conversions. By default, mcs::units allows _explicit_ conversion in quantity constructors, but implicit conversions are prohibited unless specifically enabled via #define MCS_UNITS_ENABLE_IMPLICIT_CONVERSIONS
At best, it appears to me that how I use the dimension/unit library is very different from what everybody is doing. So I will retire from the
I don't think so. Matthias

Matthias Schabel wrote:
1) I do not need a library that insists on defining some "system of units" to which everything gets converted.
A major design goal of mcs::units was to allow users to work in a system that matches their problem. These can be physical units, but can be anything else, also...
But I don't want the user of the library to have to define a "system" of units just to use a function in the library. If there is, say, a function that takes a distance, a mass, and a time and does computations with them, the user should be able to feed into that function arbitrary units for each of the three without having to use the dimensions library to define a system first. So the thing I didn't like in your version of my sample code is the need for a template parameter called "System". I think in another post you yourself indicate how these defined-by-convention "systems" of units used by physicists cause some awkwardness, because a system might not define the units for momentum to be equal to the units of mass times the units of distance divided by the units of time, creating the need for extra conversions. I am pretty badly prejudiced as a mathematician. I think within a single code module, one should forget these arbitrary man-made conventions and code using mathematically consistent units (so the units for the product of two dimensions are always the product of the the units of the two dimensions, etc.). This yields in my opinion much cleaner mathematical formulas with simpler constants and therefore cleaner code as well. My view is that conventions like SI units should appear only in the interface to the module, and the first thing that the module should do on entry is to convert everything to a mathematically consistent set of units, rather than a set conforming to a rather arbitrary convention. (I'm sorry if I sound like a closed-minded cranky old mathematician, but that's exactly what I am.)

Hi Deane, I know you're a smart guy, and I want to understand your view on this stuff. Here are a few definitions to simplify the discussion: dimension - a set of rational powers of abstract measures (length, time, mass...) unit - a specific measure of a dimension (meters, hours, etc...) quantity - a quantity of a unit (5.6 meters, 7 hours, etc...) Let's consider the diffusion equation that you have mentioned on a couple of occasions, in 1D: dU/dt = k d^2U/dx^2 Let's call the dimension associated with U D{U}, that with t D{t}, and that with x D{x}. Then, for this to be dimensionally correct, the dimension of k needs to be : D{k} = D{x}^2 / ( D{U} D{t} ) For heat conduction, U=temperature, t=time, x=distance, and k=thermal diffusivity, but, as you point out, these could represent anything you wanted. Now, if you just wanted to perform dimensional analysis on this equation to determine, for example the correct dimension of k, it is a strictly formal operation; the next version will include runtime capability in the dimension<> class to allow operations like this: const length_type x; const temperature_type U; const time_type t; std::cout << pow<2>(x)/(U*t) << std::endl; to get this output: [L]^2 [T]^-1 [t]^-1 This is analogous to the computations done in the unit<> class, but with no associated unit system. However, if you actually want to compute a numerical value from this equation, you have to specify what unit you use to measure the various quantities. These units do not, of course, all have to belong to any specific unit system, but the value of the thermal diffusivity constant, k, will depend on the specific combination of units used. To be completely concrete, imagine that you have an experimental apparatus that consists of a long, thin metal bar that you are heating from one end, and you would like to determine the thermal diffusivity constant by regressing your measurements of the temperature at some point along the bar as a function of time to the theoretical expression. To do this, you need a function that computes the solution to this equation as a function of t: temperature end_of_bar_temperature(time t,position x) { return some_function_depending_on_k(t,x); } However, the value of k depends on the units you use to measure times and temperatures, so, if you write this function to take any unit of time, and output any unit of temperature, within the function you need to compute the value of k for those particular units. To do that, you need to have a value of k that is defined for _some_ triple of length, time, and temperature units, and you need to decide how to choose what unit system you are going to use do the calculation (where unit system in this context means any triple of length, time, and temperature units - it could be SI or CGS, but could also be furlongs, hours, and Rankine if you wanted it to be). The point is that the calculation needs to be congruent with the unit system that the constant k is defined in...
But I don't want the user of the library to have to define a "system" of units just to use a function in the library. If there is, say, a function that takes a distance, a mass, and a time and does computations with them, the user should be able to feed into that function arbitrary units for each of the three without having to use the dimensions library to define a system first.
As far as I can see, there are only three options: 1) pure dimensional analysis, where you don't consider the units or any numerical values, and just analyze dimensional correctness 2) choosing a particular unit system in which dimensional constants are defined and computing in that system, with no unit conversions 3) accepting any combination of units with the correct dimensions, then converting internally
So the thing I didn't like in your version of my sample code is the need for a template parameter called "System".
I'm working with Steven Watanabe on a demo implementation of the library that allows heterogeneous units from different systems to be mixed, so each dimension carries its own system information along with it. The main problem with this is that it these heterogeneous composite units make it difficult to define functions that are restricted at compile time to a specific unit system. With concept extensions to C++, it ought to be possible to mitigate this issue, but at the moment I don't see an easy way to get both features at the same time...
I think in another post you yourself indicate how these defined-by-convention "systems" of units used by physicists cause some awkwardness, because a system might not define the units for momentum to be equal to the units of mass times the units of distance divided by the units of time, creating the need for extra conversions.
I don't understand what you're getting at here... Matthias

Matthias Schabel wrote:
The point is that the calculation needs to be congruent with the unit system that the constant k is defined in...
Thus my desire for the xxx * unit that would convert k from the units it is written in to the unit system being used. There are times when you won't want to do this if precision is an issue but in the main, if there's a conversion somewhere to get into the units used by this EQ and out, you're loosing precision anyway. I wonder, have you read "Object-oriented units of measurement" by Allen et al?
I think in another post you yourself indicate how these defined-by-convention "systems" of units used by physicists cause some awkwardness, because a system might not define the units for momentum to be equal to the units of mass times the units of distance divided by the units of time, creating the need for extra conversions.
I don't understand what you're getting at here...
IE, gallons for volume. There are different definitions of gallon, some relate to L^3, some do not. There are other units in some dimensions that are not derived from any component dimension's units.

Thus my desire for the xxx * unit that would convert k from the units it is written in to the unit system being used. There are times when you won't want to do this if precision is an issue but in the main, if there's a conversion somewhere to get into the units used by this EQ and out, you're loosing precision anyway.
Right now you can do this via explicit constructor: consider this: static const quantity<SI::area> k(1.5*square_meters); quantity<CGS::length> f(const quantity<CGS::length>& l) { return quantity<CGS::area>(k)/l; } The main limitation at the moment, which I believe I have figured out how to eliminate, is that all terms in a unit must belong to the same unit system. It may take a while to get heterogeneous units and conversions working correctly, but I now see how it is possible to do that without negatively impacting the ability to require function arguments that belong to a homogeneous system. That is, I think it should be possible to have both of these behaviors: template<class System> void f(const quantity< unit<System,dimension_type> >& q) void f(const quantity< SI::dimension >& q) function as expected, where the former takes any quantity of the specified dimension_type and the latter only takes homogeneous quantities of the specified dimension type in the SI system. System in this context can be homogeneous, so you could pass something like this to the first form: f(1.5*feet*cm) and have areas specified in ft cm units with no conversions... Then, you could do this to convert : quantity<SI::area> qq(1.5*feet*cm); to get the equivalent in square meters.
I wonder, have you read "Object-oriented units of measurement" by Allen et al?
I have briefly looked at it - they have more tolerance for runtime overhead than I do, which significantly simplifies the implementation issues.
IE, gallons for volume. There are different definitions of gallon, some relate to L^3, some do not. There are other units in some dimensions that are not derived from any component dimension's units.
I think that the best I am going to be able to do is to have each variant live in its own "system" - if you don't like that nomenclature, you can just consider the system to be a tag that allows you to differentiate between differing units with the same dimensional signatures: struct inch_system : public ordinal<1> { }; struct foot_system : public ordinal<2> { }; struct yard_system : public ordinal<3> { }; ... conversions between the various lengths ... Ultimately, my opinions on this problem domain are 1) The vast majority of users of this library will just convert from other units into SI units for internal computations; this is most easily accomplished by simply defining constants for the irregular units in terms of SI units. 2) There are various compromises to be made in choosing how to implement variant unit systems, and there is no one-size-fits-all solution, so the library needs to be as flexible as possible in allowing knowledgeable developers to implement something that meets their needs. 3) There are many ways to skin this cat, each with advantages and disadvantages. I don't think the library is going to be able to provide an out-of- the-box solution for every reasonable use case. Therefore, I want to provide a functional solution for the most common case of (internationally standardized) SI units. Conversion factors for various commonly used non-SI units could be included as constants in SI units. Matthias

AMDG Matthias Schabel <boost <at> schabel-family.org> writes:
I'm working with Steven Watanabe on a demo implementation of the library that allows heterogeneous units from different systems to be mixed, so each dimension carries its own system information along with it. The main problem with this is that it these heterogeneous composite units make it difficult to define functions that are restricted at compile time to a specific unit system. With concept extensions to C++, it ought to be possible to mitigate this issue, but at the moment I don't see an easy way to get both features at the same time...
It can be done using SFINAE. However, 1) All the dimension lists need to be processed twice. 2) Partial ordering doesn't work. 2 is solved by concepts. 1 is not. I've been wondering whether that information can be encoded in the system. Here are the basics for when each fundamental dimension has a unique unit. template<class DimensionMap> struct heterogeneous_system {}; template<class DimensionMap> template<class DimensionMap, class Tag> struct unit_info<heterogeneous_system<DimensionMap>, Tag> : unit_info<typename mpl::at<DimensionMap, Tag>::type, Tag> { }; template<class DimensionMap, class Tag, class System> struct base_unit_converter<Tag,heterogeneous_system<DimensionMap>,System> : base_unit_converter<Tag, typename mpl::at<DimensionMap, Tag>::type,System> { }; namespace MGS { typedef heterogeneous_system< mpl::map< mpl::pair<length_tag, SI::system>, mpl::pair<time_tag, SI::system>, mpl::pair<mass_tag, CGS::system> >
system;
typedef unit<system, energy_type> energy; energy g_m_squared_per_sec_squared; } In Christ, Steven Watanabe

Hi Steven,
It can be done using SFINAE. However, 1) All the dimension lists need to be processed twice. 2) Partial ordering doesn't work.
2 is solved by concepts. 1 is not.
I agree.
I've been wondering whether that information can be encoded in the system. Here are the basics for when each fundamental dimension has a unique unit.
This is exactly what I concluded is the way to solve the problems with dimension_tag being a triple... What we need is 1) a way to make it possible to use the library in exactly the way it is used in the current version when the system is homogeneous (each dimension is represented by the same system) 2) gracefully handles heterogeneous systems, reducing them to homogeneous when possible For 1), I think we can define a specific class to represent a homogeneous system: template<class S> struct homogeneous_system<S> { ... guarantees that S is the same for all dimensions... }; We can just overload all the unit operations on this class template<class System,class Dim> struct multiply_typeof_helper<unit<homogeneous_system<System>,Dim>, unit<homogeneous_system<System>,Dim> > to get our current functionality, then have more general specializations that work on heterogeneous systems or mixtures of homogeneous and heterogeneous systems: template<class System,class Dim> struct multiply_typeof_helper<unit<heterogeneous_system <System>,Dim>, unit<heterogeneous_system<System>,Dim> > you suggest template<class S> struct heterogeneous_system<S> { ... guarantees that S is a list of at least 2 different systems };
template<class DimensionMap> struct heterogeneous_system {};
template<class DimensionMap>
template<class DimensionMap, class Tag> struct unit_info<heterogeneous_system<DimensionMap>, Tag> : unit_info<typename mpl::at<DimensionMap, Tag>::type, Tag> { };
template<class DimensionMap, class Tag, class System> struct base_unit_converter<Tag,heterogeneous_system<DimensionMap>,System> : base_unit_converter<Tag, typename mpl::at<DimensionMap, Tag>::type,System> { };
namespace MGS {
typedef heterogeneous_system< mpl::map< mpl::pair<length_tag, SI::system>, mpl::pair<time_tag, SI::system>, mpl::pair<mass_tag, CGS::system>
system;
typedef unit<system, energy_type> energy;
energy g_m_squared_per_sec_squared;
}
I like this - this looks like a slick solution. What I would do is have the typedef resolve to homogeneous_system<> in the case where all the system tags are identical: struct system_resolver<S> { typedef mpl::map< mpl::pair<length_tag,SI::system>, mpl::pair<time_tag,SI::system>, mpl::pair<mass_tag,SI::system> > >::type gives homogeneous_system<SI::system> and typedef system_resolver< mpl::map< mpl::pair<length_tag,SI::system>, mpl::pair<time_tag,CGS::system>, mpl::pair<mass_tag,SI::system> > >::type gives typedef heterogeneous_system< mpl::map< mpl::pair<length_tag,SI::system>, mpl::pair<mass_tag,SI::system>, mpl::pair<time_tag,CGS::system> > >::type where order is unambiguous - sorted first on system, then on tag. This way, any unit system also resolves to a single, well-defined type that you can overload on... void f(quantity< unit<homogeneous_system<S>,length_type> >) void f(quantity< unit<heterogeneous_system<whatever>,length_type> >) In addition, this way we should not incur any additional compile-time overhead for adding this if the library user doesn't have any heterogeneous units. I'm going to work on modifying the existing code to work with a homogeneous_system type... Maybe we can have it all... Matthias

Steven,
template<class DimensionMap, class Tag, class System> struct base_unit_converter<Tag,heterogeneous_system<DimensionMap>,System> : base_unit_converter<Tag, typename mpl::at<DimensionMap, Tag>::type,System> { };
I actually think we can just leave the base_unit_converter template alone since this deals with a converting values of one fundamental dimension between two systems. Then we can just specialize conversion_helper for homogeneous systems to be the current implementation and have the default specialization deal with heterogeneous systems: // current implementation for homogeneous systems template<class S1, class S2, class Dim1, class Y> class conversion_helper< quantity<unit<homogeneous_system<S1>,Dim1>,Y>, quantity<unit<homogeneous_system<S2>,Dim1>,Y> > // generic implementation for heterogeneous systems template<class System1,class Dim1, class System2,class Dim2, class Y> class conversion_helper< quantity<unit<System1,Dim1>,Y>, quantity<unit<System2,Dim2>,Y> > The actual implementation of the latter will probably be the hardest part... I've plugged in specializations for homogeneous_system<S> into the code base and all examples still work correctly. Once we have a functioning system that properly sorts heterogeneous units, we should just have to write specializations for struct multiply_typeof_helper< unit< heterogeneous_system<S>,Dim1>, unit< heterogeneous_system<S>,Dim2> > and struct divide_typeof_helper< unit< heterogeneous_system<S>,Dim1>, unit< heterogeneous_system<S>,Dim2> > unary_plus_typeof_helper, unary_minus_typeof_helper, add_typeof_helper, subtract_typeof_helper, power_typeof_helper, and root_typeof_helper all stay the same as far as I can see... I'm starting to feel optimistic that we'll be able to get something very flexible without adding too much complexity... Matthias

template<class DimensionMap, class Tag> struct unit_info<heterogeneous_system<DimensionMap>, Tag> : unit_info<typename mpl::at<DimensionMap, Tag>::type, Tag> { };
namespace MGS {
typedef heterogeneous_system< mpl::map< mpl::pair<length_tag, SI::system>, mpl::pair<time_tag, SI::system>, mpl::pair<mass_tag, CGS::system>
There seems to be a problem with this, actually : what if I want replicate tags in different systems? typedef heterogeneous_system< mpl::map< mpl::pair<length_tag,SI::system>, mpl::pair<length_tag,CGS::system> > > Then the mpl::at becomes a problem, I believe... Matthias

AMDG Matthias Schabel <boost <at> schabel-family.org> writes:
There seems to be a problem with this, actually : what if I want replicate tags in different systems?
typedef heterogeneous_system< mpl::map< mpl::pair<length_tag,SI::system>, mpl::pair<length_tag,CGS::system> > >
Then the mpl::at becomes a problem, I believe...
For this case we need to make the system store basically all the information that we were putting into the dimension list. I think this solution to be better because overloading by dimension is easy and the extra work would probably have to be done anyway. In Christ, Steven Watanabe

There seems to be a problem with this, actually : what if I want replicate tags in different systems?
typedef heterogeneous_system< mpl::map< mpl::pair<length_tag,SI::system>, mpl::pair<length_tag,CGS::system> > >
Then the mpl::at becomes a problem, I believe...
For this case we need to make the system store basically all the information that we were putting into the dimension list. I think this solution to be better because overloading by dimension is easy and the extra work would probably have to be done anyway.
You're probably right - limiting heterogeneous systems to one system per tag is a reasonable constraint and will make the implementation issues much less sticky for both operators and conversions... Matthias

Matthias Schabel wrote:
Hi Deane,
I know you're a smart guy, and I want to understand your view on this stuff.
Matthias, I'm sorry for not responding to your posting. I will try to do so if I can squeeze in the time. Regards, Deane

Deane Yang wrote:
Matthias Schabel wrote:
1) I do not need a library that insists on defining some "system of units" to which everything gets converted. A major design goal of mcs::units was to allow users to work in a system that matches their problem. These can be physical units, but can be anything else, also...
But I don't want the user of the library to have to define a "system" of units just to use a function in the library. If there is, say, a function that takes a distance, a mass, and a time and does computations with them, the user should be able to feed into that function arbitrary units for each of the three without having to use the dimensions library to define a system first.
In order to use the function the library would have to expose the quantity type it uses - as such it would expose the system as well. It would not be required to define the system yourself. On the other hand, unless you write your own code in their system you'll have to write conversion helpers to get into their system and of course you have to write your own system. Even if you both use, say SI, but define it yourselves you'll have to write helpers...they will do nothing of importance but will be necessary I believe as you'll have unrelated types that "mean" the same thing. It's certainly not foolproof but I think the idea of a system has merit. It allows someone to write a library in say, SI, and provide the interface to a user who assumes some other set of standard units and the two will work together fairly easily.

imperial::feet->SI::meters CGS::centimeters->SI::meters
to get the conversion factor...this is unambiguous in all cases, AFAICS.
I didn't mean that it shouldn't be valid. I meant that both should be allowed. Otherwise, conversions are not necessarily reversible.
Right - I believe it is unambiguous in both directions since there has to be a one-to-one mapping of source and destination dimensions... I just misunderstood what you were saying. Matthias ---------------------------------------------------------------- Matthias Schabel 2859 Glen Oaks Drive Salt Lake City, UT 84109 801-706-5760 (cell) 801-484-0811 (home) matthias at stanfordalumni dot org ---------------------------------------------------------------- 
participants (6)
-
Deane Yang
-
Matthias Schabel
-
Michael Fawcett
-
Noah Roberts
-
Peter Dimov
-
Steven Watanabe